@liveblocks/client
Advanced tools
Comparing version 0.12.0-beta.13 to 0.12.0-beta.14
@@ -12,4 +12,7 @@ import { Op, SerializedCrdtWithId, SerializedList } from "./live"; | ||
getItem(id: string): AbstractCrdt | undefined; | ||
apply(op: Op): void; | ||
apply(ops: Op[]): Op[]; | ||
get root(): LiveObject<T>; | ||
addToUndoStack(ops: Op[]): void; | ||
undo(): void; | ||
redo(): void; | ||
count(): number; | ||
@@ -36,8 +39,13 @@ generateId(): string; | ||
*/ | ||
_setParent(parent: AbstractCrdt): void; | ||
get _parentKey(): string | undefined; | ||
_apply(op: Op): Op[]; | ||
/** | ||
* INTERNAL | ||
*/ | ||
_setParentLink(parent: AbstractCrdt, key: string): void; | ||
/** | ||
* INTERNAL | ||
*/ | ||
_attach(id: string, doc: Doc): void; | ||
abstract _attachChild(id: string, key: string, crdt: AbstractCrdt): void; | ||
abstract _attachChild(id: string, key: string, crdt: AbstractCrdt): Op[]; | ||
/** | ||
@@ -48,9 +56,29 @@ * INTERNAL | ||
abstract _detachChild(crdt: AbstractCrdt): void; | ||
/** | ||
* Subscribes to updates. | ||
*/ | ||
subscribe(listener: () => void): void; | ||
/** | ||
* Subscribes to updates and children updates. | ||
*/ | ||
subscribeDeep(listener: () => void): void; | ||
/** | ||
* Unsubscribes to updates. | ||
*/ | ||
unsubscribe(listener: () => void): void; | ||
/** | ||
* Unsubscribes to updates and children updates. | ||
*/ | ||
unsubscribeDeep(listener: () => void): void; | ||
notify(onlyDeep?: boolean): void; | ||
/** | ||
* INTERNAL | ||
*/ | ||
_notify(onlyDeep?: boolean): void; | ||
abstract _serialize(parentId: string, parentKey: string): Op[]; | ||
} | ||
/** | ||
* The LiveObject class is similar to a JavaScript object that is synchronized on all clients. | ||
* Keys should be a string, and values should be serializable to JSON. | ||
* If multiple clients update the same property simultaneously, the last modification received by the Liveblocks servers is the winner. | ||
*/ | ||
export declare class LiveObject<T extends Record<string, any> = Record<string, any>> extends AbstractCrdt { | ||
@@ -76,3 +104,3 @@ #private; | ||
*/ | ||
_attachChild(id: string, key: keyof T, child: AbstractCrdt): void; | ||
_attachChild(id: string, key: keyof T, child: AbstractCrdt): Op[]; | ||
/** | ||
@@ -89,8 +117,29 @@ * INTERNAL | ||
*/ | ||
_apply(op: Op): void; | ||
_apply(op: Op): Op[]; | ||
/** | ||
* Transform the LiveObject into a javascript object | ||
*/ | ||
toObject(): T; | ||
/** | ||
* Adds or updates a property with a specified key and a value. | ||
* @param key The key of the property to add | ||
* @param value The value of the property to add | ||
*/ | ||
set<TKey extends keyof T>(key: TKey, value: T[TKey]): void; | ||
/** | ||
* Returns a specified property from the LiveObject. | ||
* @param key The key of the property to get | ||
*/ | ||
get<TKey extends keyof T>(key: TKey): T[TKey]; | ||
/** | ||
* Adds or updates multiple properties at once with an object. | ||
* @param overrides The object used to overrides properties | ||
*/ | ||
update(overrides: Partial<T>): void; | ||
} | ||
/** | ||
* The LiveMap class is similar to a JavaScript Map that is synchronized on all clients. | ||
* Keys should be a string, and values should be serializable to JSON. | ||
* If multiple clients update the same property simultaneously, the last modification received by the Liveblocks servers is the winner. | ||
*/ | ||
export declare class LiveMap<TKey extends string, TValue> extends AbstractCrdt { | ||
@@ -114,3 +163,3 @@ #private; | ||
*/ | ||
_attachChild(id: string, key: TKey, child: AbstractCrdt): void; | ||
_attachChild(id: string, key: TKey, child: AbstractCrdt): Op[]; | ||
/** | ||
@@ -124,13 +173,54 @@ * INTERNAL | ||
_detachChild(child: AbstractCrdt): void; | ||
/** | ||
* Returns a specified element from the LiveMap. | ||
* @param key The key of the element to return. | ||
* @returns The element associated with the specified key, or undefined if the key can't be found in the LiveMap. | ||
*/ | ||
get(key: TKey): TValue | undefined; | ||
/** | ||
* Adds or updates an element with a specified key and a value. | ||
* @param key The key of the element to add. Should be a string. | ||
* @param value The value of the element to add. Should be serializable to JSON. | ||
*/ | ||
set(key: TKey, value: TValue): void; | ||
/** | ||
* Returns the number of elements in the LiveMap. | ||
*/ | ||
get size(): number; | ||
/** | ||
* Returns a boolean indicating whether an element with the specified key exists or not. | ||
* @param key The key of the element to test for presence. | ||
*/ | ||
has(key: TKey): boolean; | ||
/** | ||
* Removes the specified element by key. | ||
* @param key The key of the element to remove. | ||
* @returns true if an element existed and has been removed, or false if the element does not exist. | ||
*/ | ||
delete(key: TKey): boolean; | ||
/** | ||
* Returns a new Iterator object that contains the [key, value] pairs for each element. | ||
*/ | ||
entries(): IterableIterator<[string, TValue]>; | ||
/** | ||
* Same function object as the initial value of the entries method. | ||
*/ | ||
[Symbol.iterator](): IterableIterator<[string, TValue]>; | ||
/** | ||
* Returns a new Iterator object that contains the keys for each element. | ||
*/ | ||
keys(): IterableIterator<TKey>; | ||
/** | ||
* Returns a new Iterator object that contains the values for each element. | ||
*/ | ||
values(): IterableIterator<TValue>; | ||
/** | ||
* Executes a provided function once per each key/value pair in the Map object, in insertion order. | ||
* @param callback Function to execute for each entry in the map. | ||
*/ | ||
forEach(callback: (value: TValue, key: TKey, map: LiveMap<TKey, TValue>) => void): void; | ||
} | ||
/** | ||
* The LiveList class represents an ordered collection of items that is synchorinized across clients. | ||
*/ | ||
export declare class LiveList<T> extends AbstractCrdt { | ||
@@ -158,3 +248,3 @@ #private; | ||
*/ | ||
_attachChild(id: string, key: string, child: AbstractCrdt): void; | ||
_attachChild(id: string, key: string, child: AbstractCrdt): Op[]; | ||
/** | ||
@@ -168,17 +258,96 @@ * INTERNAL | ||
_setChildKey(key: string, child: AbstractCrdt): void; | ||
/** | ||
* INTERNAL | ||
*/ | ||
_apply(op: Op): Op[]; | ||
/** | ||
* Returns the number of elements. | ||
*/ | ||
get length(): number; | ||
push(item: T): void; | ||
insert(item: T, index: number): void; | ||
/** | ||
* Adds one element to the end of the LiveList. | ||
* @param element The element to add to the end of the LiveList. | ||
*/ | ||
push(element: T): void; | ||
/** | ||
* Inserts one element at a specified index. | ||
* @param element The element to insert. | ||
* @param index The index at which you want to insert the element. | ||
*/ | ||
insert(element: T, index: number): void; | ||
/** | ||
* Move one element from one index to another. | ||
* @param index The index of the element to move | ||
* @param targetIndex The index where the element should be after moving. | ||
*/ | ||
move(index: number, targetIndex: number): void; | ||
/** | ||
* Deletes an element at the specified index | ||
* @param index The index of the element to delete | ||
*/ | ||
delete(index: number): void; | ||
/** | ||
* Returns an Array of all the elements in the LiveList. | ||
*/ | ||
toArray(): T[]; | ||
/** | ||
* Tests whether all elements pass the test implemented by the provided function. | ||
* @param predicate Function to test for each element, taking two arguments (the element and its index). | ||
* @returns true if the predicate function returns a truthy value for every element. Otherwise, false. | ||
*/ | ||
every(predicate: (value: T, index: number) => unknown): boolean; | ||
/** | ||
* Creates an array with all elements that pass the test implemented by the provided function. | ||
* @param predicate Function to test each element of the LiveList. Return a value that coerces to true to keep the element, or to false otherwise. | ||
* @returns An array with the elements that pass the test. | ||
*/ | ||
filter(predicate: (value: T, index: number) => unknown): T[]; | ||
/** | ||
* Returns the first element that satisfies the provided testing function. | ||
* @param predicate Function to execute on each value. | ||
* @returns The value of the first element in the LiveList that satisfies the provided testing function. Otherwise, undefined is returned. | ||
*/ | ||
find(predicate: (value: T, index: number) => unknown): T | undefined; | ||
/** | ||
* Returns the index of the first element in the LiveList that satisfies the provided testing function. | ||
* @param predicate Function to execute on each value until the function returns true, indicating that the satisfying element was found. | ||
* @returns The index of the first element in the LiveList that passes the test. Otherwise, -1. | ||
*/ | ||
findIndex(predicate: (value: T, index: number) => unknown): number; | ||
/** | ||
* Executes a provided function once for each element. | ||
* @param callbackfn Function to execute on each element. | ||
*/ | ||
forEach(callbackfn: (value: T, index: number) => void): void; | ||
/** | ||
* Get the element at the specified index. | ||
* @param index The index on the element to get. | ||
* @returns The element at the specified index or undefined. | ||
*/ | ||
get(index: number): T | undefined; | ||
/** | ||
* Returns the first index at which a given element can be found in the LiveList, or -1 if it is not present. | ||
* @param searchElement Element to locate. | ||
* @param fromIndex The index to start the search at. | ||
* @returns The first index of the element in the LiveList; -1 if not found. | ||
*/ | ||
indexOf(searchElement: T, fromIndex?: number): number; | ||
/** | ||
* Returns the last index at which a given element can be found in the LiveList, or -1 if it is not present. The LiveLsit is searched backwards, starting at fromIndex. | ||
* @param searchElement Element to locate. | ||
* @param fromIndex The index at which to start searching backwards. | ||
* @returns | ||
*/ | ||
lastIndexOf(searchElement: T, fromIndex?: number): number; | ||
/** | ||
* Creates an array populated with the results of calling a provided function on every element. | ||
* @param callback Function that is called for every element. | ||
* @returns An array with each element being the result of the callback function. | ||
*/ | ||
map<U>(callback: (value: T, index: number) => U): U[]; | ||
/** | ||
* Tests whether at least one element in the LiveList passes the test implemented by the provided function. | ||
* @param predicate Function to test for each element. | ||
* @returns true if the callback function returns a truthy value for at least one element. Otherwise, false. | ||
*/ | ||
some(predicate: (value: T, index: number) => unknown): boolean; | ||
@@ -185,0 +354,0 @@ [Symbol.iterator](): IterableIterator<T>; |
@@ -13,3 +13,3 @@ "use strict"; | ||
}; | ||
var _Doc_instances, _Doc_clock, _Doc_opClock, _Doc_items, _Doc_root, _Doc_actor, _Doc_dispatch, _Doc_applyCreateRegister, _Doc_applyDeleteRecordKey, _Doc_applyUpdateRecord, _Doc_applyCreateMap, _Doc_applyCreateList, _Doc_applyCreateObject, _Doc_applyDeleteRecord, _Doc_applySetParentKey, _AbstractCrdt_listeners, _AbstractCrdt_deepListeners, _AbstractCrdt_parent, _AbstractCrdt_doc, _AbstractCrdt_id, _LiveObject_map, _LiveObject_propToLastUpdate, _LiveMap_map, _LiveRegister_data, _LiveList_items, _LiveListIterator_innerIterator; | ||
var _Doc_instances, _Doc_clock, _Doc_opClock, _Doc_items, _Doc_root, _Doc_actor, _Doc_dispatch, _Doc_undoStack, _Doc_redoStack, _Doc_applyOp, _AbstractCrdt_instances, _AbstractCrdt_listeners, _AbstractCrdt_deepListeners, _AbstractCrdt_parent, _AbstractCrdt_doc, _AbstractCrdt_id, _AbstractCrdt_parentKey, _AbstractCrdt_applySetParentKey, _AbstractCrdt_applyRegister, _AbstractCrdt_applyCreateObject, _AbstractCrdt_applyCreateMap, _AbstractCrdt_applyCreateList, _LiveObject_instances, _LiveObject_map, _LiveObject_propToLastUpdate, _LiveObject_applyDeleteObjectKey, _LiveMap_map, _LiveRegister_data, _LiveList_items, _LiveListIterator_innerIterator; | ||
Object.defineProperty(exports, "__esModule", { value: true }); | ||
@@ -21,2 +21,3 @@ exports.LiveList = exports.LiveMap = exports.LiveObject = exports.Doc = void 0; | ||
function noOp() { } | ||
const MAX_UNDO_STACK = 50; | ||
class Doc { | ||
@@ -31,2 +32,4 @@ constructor(root, actor = 0, dispatch = noOp) { | ||
_Doc_dispatch.set(this, void 0); | ||
_Doc_undoStack.set(this, []); | ||
_Doc_redoStack.set(this, []); | ||
__classPrivateFieldSet(this, _Doc_root, root, "f"); | ||
@@ -72,2 +75,3 @@ __classPrivateFieldSet(this, _Doc_actor, actor, "f"); | ||
dispatch(ops) { | ||
__classPrivateFieldSet(this, _Doc_redoStack, [], "f"); | ||
__classPrivateFieldGet(this, _Doc_dispatch, "f").call(this, ops); | ||
@@ -84,37 +88,8 @@ } | ||
} | ||
apply(op) { | ||
switch (op.type) { | ||
case live_1.OpType.UpdateObject: { | ||
__classPrivateFieldGet(this, _Doc_instances, "m", _Doc_applyUpdateRecord).call(this, op); | ||
break; | ||
} | ||
case live_1.OpType.CreateObject: { | ||
__classPrivateFieldGet(this, _Doc_instances, "m", _Doc_applyCreateObject).call(this, op); | ||
break; | ||
} | ||
case live_1.OpType.CreateMap: { | ||
__classPrivateFieldGet(this, _Doc_instances, "m", _Doc_applyCreateMap).call(this, op); | ||
break; | ||
} | ||
case live_1.OpType.CreateList: { | ||
__classPrivateFieldGet(this, _Doc_instances, "m", _Doc_applyCreateList).call(this, op); | ||
break; | ||
} | ||
case live_1.OpType.DeleteCrdt: { | ||
__classPrivateFieldGet(this, _Doc_instances, "m", _Doc_applyDeleteRecord).call(this, op); | ||
break; | ||
} | ||
case live_1.OpType.SetParentKey: { | ||
__classPrivateFieldGet(this, _Doc_instances, "m", _Doc_applySetParentKey).call(this, op); | ||
break; | ||
} | ||
case live_1.OpType.DeleteObjectKey: { | ||
__classPrivateFieldGet(this, _Doc_instances, "m", _Doc_applyDeleteRecordKey).call(this, op); | ||
break; | ||
} | ||
case live_1.OpType.CreateRegister: { | ||
__classPrivateFieldGet(this, _Doc_instances, "m", _Doc_applyCreateRegister).call(this, op); | ||
break; | ||
} | ||
apply(ops) { | ||
const reverse = []; | ||
for (const op of ops) { | ||
reverse.push(...__classPrivateFieldGet(this, _Doc_instances, "m", _Doc_applyOp).call(this, op)); | ||
} | ||
return reverse; | ||
} | ||
@@ -124,2 +99,24 @@ get root() { | ||
} | ||
addToUndoStack(ops) { | ||
if (__classPrivateFieldGet(this, _Doc_undoStack, "f").length >= MAX_UNDO_STACK) { | ||
__classPrivateFieldGet(this, _Doc_undoStack, "f").shift(); | ||
} | ||
__classPrivateFieldGet(this, _Doc_undoStack, "f").push(ops); | ||
} | ||
undo() { | ||
const ops = __classPrivateFieldGet(this, _Doc_undoStack, "f").pop(); | ||
if (ops == null) { | ||
return; | ||
} | ||
__classPrivateFieldGet(this, _Doc_redoStack, "f").push(this.apply(ops)); | ||
__classPrivateFieldGet(this, _Doc_dispatch, "f").call(this, ops); | ||
} | ||
redo() { | ||
const ops = __classPrivateFieldGet(this, _Doc_redoStack, "f").pop(); | ||
if (ops == null) { | ||
return; | ||
} | ||
__classPrivateFieldGet(this, _Doc_undoStack, "f").push(this.apply(ops)); | ||
__classPrivateFieldGet(this, _Doc_dispatch, "f").call(this, ops); | ||
} | ||
count() { | ||
@@ -138,83 +135,32 @@ return __classPrivateFieldGet(this, _Doc_items, "f").size; | ||
exports.Doc = Doc; | ||
_Doc_clock = new WeakMap(), _Doc_opClock = new WeakMap(), _Doc_items = new WeakMap(), _Doc_root = new WeakMap(), _Doc_actor = new WeakMap(), _Doc_dispatch = new WeakMap(), _Doc_instances = new WeakSet(), _Doc_applyCreateRegister = function _Doc_applyCreateRegister(op) { | ||
if (__classPrivateFieldGet(this, _Doc_items, "f").has(op.id)) { | ||
return; | ||
} | ||
const parent = __classPrivateFieldGet(this, _Doc_items, "f").get(op.parentId); | ||
if (parent == null) { | ||
return; | ||
} | ||
if (!(parent instanceof LiveMap) && !(parent instanceof LiveList)) { | ||
throw new Error("LiveRegister can only be attached to a LiveMap or LiveList"); | ||
} | ||
const newRegister = new LiveRegister(op.data); | ||
parent._attachChild(op.id, op.parentKey, newRegister); | ||
}, _Doc_applyDeleteRecordKey = function _Doc_applyDeleteRecordKey(op) { | ||
const item = __classPrivateFieldGet(this, _Doc_items, "f").get(op.id); | ||
if (item && item instanceof LiveObject) { | ||
item._apply(op); | ||
} | ||
}, _Doc_applyUpdateRecord = function _Doc_applyUpdateRecord(op) { | ||
const item = __classPrivateFieldGet(this, _Doc_items, "f").get(op.id); | ||
if (item && item instanceof LiveObject) { | ||
item._apply(op); | ||
} | ||
}, _Doc_applyCreateMap = function _Doc_applyCreateMap(op) { | ||
if (__classPrivateFieldGet(this, _Doc_items, "f").has(op.id)) { | ||
return; | ||
} | ||
const parent = __classPrivateFieldGet(this, _Doc_items, "f").get(op.parentId); | ||
if (parent == null) { | ||
return; | ||
} | ||
const newMap = new LiveMap(); | ||
parent._attachChild(op.id, op.parentKey, newMap); | ||
}, _Doc_applyCreateList = function _Doc_applyCreateList(op) { | ||
if (__classPrivateFieldGet(this, _Doc_items, "f").has(op.id)) { | ||
return; | ||
} | ||
const parent = __classPrivateFieldGet(this, _Doc_items, "f").get(op.parentId); | ||
if (parent == null) { | ||
return; | ||
} | ||
const list = new LiveList(); | ||
parent._attachChild(op.id, op.parentKey, list); | ||
}, _Doc_applyCreateObject = function _Doc_applyCreateObject(op) { | ||
if (__classPrivateFieldGet(this, _Doc_items, "f").has(op.id)) { | ||
return; | ||
} | ||
if (op.parentId && op.parentKey) { | ||
const parent = __classPrivateFieldGet(this, _Doc_items, "f").get(op.parentId); | ||
if (parent == null) { | ||
return; | ||
_Doc_clock = new WeakMap(), _Doc_opClock = new WeakMap(), _Doc_items = new WeakMap(), _Doc_root = new WeakMap(), _Doc_actor = new WeakMap(), _Doc_dispatch = new WeakMap(), _Doc_undoStack = new WeakMap(), _Doc_redoStack = new WeakMap(), _Doc_instances = new WeakSet(), _Doc_applyOp = function _Doc_applyOp(op) { | ||
switch (op.type) { | ||
case live_1.OpType.DeleteObjectKey: | ||
case live_1.OpType.UpdateObject: | ||
case live_1.OpType.DeleteCrdt: | ||
case live_1.OpType.SetParentKey: { | ||
const item = __classPrivateFieldGet(this, _Doc_items, "f").get(op.id); | ||
if (item == null) { | ||
return []; | ||
} | ||
return item._apply(op); | ||
break; | ||
} | ||
const newObj = new LiveObject(op.data); | ||
parent._attachChild(op.id, op.parentKey, newObj); | ||
case live_1.OpType.CreateList: | ||
case live_1.OpType.CreateObject: | ||
case live_1.OpType.CreateMap: | ||
case live_1.OpType.CreateRegister: { | ||
const parent = __classPrivateFieldGet(this, _Doc_items, "f").get(op.parentId); | ||
if (parent == null) { | ||
return []; | ||
} | ||
return parent._apply(op); | ||
break; | ||
} | ||
} | ||
}, _Doc_applyDeleteRecord = function _Doc_applyDeleteRecord(op) { | ||
const item = __classPrivateFieldGet(this, _Doc_items, "f").get(op.id); | ||
if (item == null) { | ||
return; | ||
} | ||
const parent = item._parent; | ||
if (parent == null) { | ||
return; | ||
} | ||
if (parent) { | ||
parent._detachChild(item); | ||
} | ||
}, _Doc_applySetParentKey = function _Doc_applySetParentKey(op) { | ||
const item = __classPrivateFieldGet(this, _Doc_items, "f").get(op.id); | ||
if (item == null) { | ||
return; | ||
} | ||
if (item._parent == null) { | ||
return; | ||
} | ||
if (item._parent instanceof LiveList) { | ||
item._parent._setChildKey(op.parentKey, item); | ||
} | ||
return []; | ||
}; | ||
class AbstractCrdt { | ||
constructor() { | ||
_AbstractCrdt_instances.add(this); | ||
_AbstractCrdt_listeners.set(this, []); | ||
@@ -225,2 +171,3 @@ _AbstractCrdt_deepListeners.set(this, []); | ||
_AbstractCrdt_id.set(this, void 0); | ||
_AbstractCrdt_parentKey.set(this, void 0); | ||
} | ||
@@ -248,6 +195,41 @@ /** | ||
*/ | ||
_setParent(parent) { | ||
if (__classPrivateFieldGet(this, _AbstractCrdt_parent, "f")) { | ||
get _parentKey() { | ||
return __classPrivateFieldGet(this, _AbstractCrdt_parentKey, "f"); | ||
} | ||
_apply(op) { | ||
switch (op.type) { | ||
case live_1.OpType.DeleteCrdt: { | ||
if (this._parent != null && this._parentKey != null) { | ||
const reverse = this._serialize(this._parent._id, this._parentKey); | ||
this._parent._detachChild(this); | ||
return reverse; | ||
} | ||
return []; | ||
} | ||
case live_1.OpType.CreateObject: { | ||
return __classPrivateFieldGet(this, _AbstractCrdt_instances, "m", _AbstractCrdt_applyCreateObject).call(this, op); | ||
} | ||
case live_1.OpType.CreateMap: { | ||
return __classPrivateFieldGet(this, _AbstractCrdt_instances, "m", _AbstractCrdt_applyCreateMap).call(this, op); | ||
} | ||
case live_1.OpType.CreateRegister: { | ||
return __classPrivateFieldGet(this, _AbstractCrdt_instances, "m", _AbstractCrdt_applyRegister).call(this, op); | ||
} | ||
case live_1.OpType.CreateList: { | ||
return __classPrivateFieldGet(this, _AbstractCrdt_instances, "m", _AbstractCrdt_applyCreateList).call(this, op); | ||
} | ||
case live_1.OpType.SetParentKey: { | ||
return __classPrivateFieldGet(this, _AbstractCrdt_instances, "m", _AbstractCrdt_applySetParentKey).call(this, op); | ||
} | ||
} | ||
return []; | ||
} | ||
/** | ||
* INTERNAL | ||
*/ | ||
_setParentLink(parent, key) { | ||
if (__classPrivateFieldGet(this, _AbstractCrdt_parent, "f") != null && __classPrivateFieldGet(this, _AbstractCrdt_parent, "f") !== parent) { | ||
throw new Error("Cannot attach parent if it already exist"); | ||
} | ||
__classPrivateFieldSet(this, _AbstractCrdt_parentKey, key, "f"); | ||
__classPrivateFieldSet(this, _AbstractCrdt_parent, parent, "f"); | ||
@@ -276,15 +258,30 @@ } | ||
} | ||
/** | ||
* Subscribes to updates. | ||
*/ | ||
subscribe(listener) { | ||
__classPrivateFieldGet(this, _AbstractCrdt_listeners, "f").push(listener); | ||
} | ||
/** | ||
* Subscribes to updates and children updates. | ||
*/ | ||
subscribeDeep(listener) { | ||
__classPrivateFieldGet(this, _AbstractCrdt_deepListeners, "f").push(listener); | ||
} | ||
/** | ||
* Unsubscribes to updates. | ||
*/ | ||
unsubscribe(listener) { | ||
(0, utils_1.remove)(__classPrivateFieldGet(this, _AbstractCrdt_listeners, "f"), listener); | ||
} | ||
/** | ||
* Unsubscribes to updates and children updates. | ||
*/ | ||
unsubscribeDeep(listener) { | ||
(0, utils_1.remove)(__classPrivateFieldGet(this, _AbstractCrdt_deepListeners, "f"), listener); | ||
} | ||
notify(onlyDeep = false) { | ||
/** | ||
* INTERNAL | ||
*/ | ||
_notify(onlyDeep = false) { | ||
if (onlyDeep === false) { | ||
@@ -299,10 +296,60 @@ for (const listener of __classPrivateFieldGet(this, _AbstractCrdt_listeners, "f")) { | ||
if (this._parent) { | ||
this._parent.notify(true); | ||
this._parent._notify(true); | ||
} | ||
} | ||
} | ||
_AbstractCrdt_listeners = new WeakMap(), _AbstractCrdt_deepListeners = new WeakMap(), _AbstractCrdt_parent = new WeakMap(), _AbstractCrdt_doc = new WeakMap(), _AbstractCrdt_id = new WeakMap(); | ||
_AbstractCrdt_listeners = new WeakMap(), _AbstractCrdt_deepListeners = new WeakMap(), _AbstractCrdt_parent = new WeakMap(), _AbstractCrdt_doc = new WeakMap(), _AbstractCrdt_id = new WeakMap(), _AbstractCrdt_parentKey = new WeakMap(), _AbstractCrdt_instances = new WeakSet(), _AbstractCrdt_applySetParentKey = function _AbstractCrdt_applySetParentKey(op) { | ||
if (this._parent == null) { | ||
return []; | ||
} | ||
if (this._parent instanceof LiveList) { | ||
const previousKey = this._parentKey; | ||
this._parent._setChildKey(op.parentKey, this); | ||
return [ | ||
{ type: live_1.OpType.SetParentKey, id: this._id, parentKey: previousKey }, | ||
]; | ||
} | ||
return []; | ||
}, _AbstractCrdt_applyRegister = function _AbstractCrdt_applyRegister(op) { | ||
if (this._doc == null) { | ||
throw new Error("Internal: doc should exist"); | ||
} | ||
if (this._doc.getItem(op.id) != null) { | ||
return []; | ||
} | ||
return this._attachChild(op.id, op.parentKey, new LiveRegister(op.data)); | ||
}, _AbstractCrdt_applyCreateObject = function _AbstractCrdt_applyCreateObject(op) { | ||
if (this._doc == null) { | ||
throw new Error("Internal: doc should exist"); | ||
} | ||
if (this._doc.getItem(op.id) != null) { | ||
return []; | ||
} | ||
return this._attachChild(op.id, op.parentKey, new LiveObject(op.data)); | ||
}, _AbstractCrdt_applyCreateMap = function _AbstractCrdt_applyCreateMap(op) { | ||
if (this._doc == null) { | ||
throw new Error("Internal: doc should exist"); | ||
} | ||
if (this._doc.getItem(op.id) != null) { | ||
return []; | ||
} | ||
return this._attachChild(op.id, op.parentKey, new LiveMap()); | ||
}, _AbstractCrdt_applyCreateList = function _AbstractCrdt_applyCreateList(op) { | ||
if (this._doc == null) { | ||
throw new Error("Internal: doc should exist"); | ||
} | ||
if (this._doc.getItem(op.id) != null) { | ||
return []; | ||
} | ||
return this._attachChild(op.id, op.parentKey, new LiveList()); | ||
}; | ||
/** | ||
* The LiveObject class is similar to a JavaScript object that is synchronized on all clients. | ||
* Keys should be a string, and values should be serializable to JSON. | ||
* If multiple clients update the same property simultaneously, the last modification received by the Liveblocks servers is the winner. | ||
*/ | ||
class LiveObject extends AbstractCrdt { | ||
constructor(object = {}) { | ||
super(); | ||
_LiveObject_instances.add(this); | ||
_LiveObject_map.set(this, void 0); | ||
@@ -313,3 +360,3 @@ _LiveObject_propToLastUpdate.set(this, new Map()); | ||
if (value instanceof AbstractCrdt) { | ||
value._setParent(this); | ||
value._setParentLink(this, key); | ||
} | ||
@@ -364,3 +411,3 @@ } | ||
const child = deserialize(entry, parentToChildren, doc); | ||
child._setParent(object); | ||
child._setParentLink(object, crdt.parentKey); | ||
__classPrivateFieldGet(object, _LiveObject_map, "f").set(crdt.parentKey, child); | ||
@@ -389,9 +436,26 @@ } | ||
const previousValue = __classPrivateFieldGet(this, _LiveObject_map, "f").get(key); | ||
let result; | ||
if (isCrdt(previousValue)) { | ||
result = previousValue._serialize(this._id, key); | ||
previousValue._detach(); | ||
} | ||
else if (previousValue === undefined) { | ||
result = [ | ||
{ type: live_1.OpType.DeleteObjectKey, id: this._id, key: key }, | ||
]; | ||
} | ||
else { | ||
result = [ | ||
{ | ||
type: live_1.OpType.UpdateObject, | ||
id: this._id, | ||
data: { [key]: previousValue }, | ||
}, | ||
]; | ||
} | ||
__classPrivateFieldGet(this, _LiveObject_map, "f").set(key, child); | ||
child._setParent(this); | ||
child._setParentLink(this, key); | ||
child._attach(id, this._doc); | ||
this.notify(); | ||
this._notify(); | ||
return result; | ||
} | ||
@@ -410,3 +474,3 @@ /** | ||
} | ||
this.notify(); | ||
this._notify(); | ||
} | ||
@@ -428,7 +492,28 @@ /** | ||
_apply(op) { | ||
var _a; | ||
if (op.type === live_1.OpType.UpdateObject) { | ||
const reverse = []; | ||
const reverseUpdate = { | ||
type: live_1.OpType.UpdateObject, | ||
id: this._id, | ||
data: {}, | ||
}; | ||
reverse.push(reverseUpdate); | ||
for (const key in op.data) { | ||
const oldValue = __classPrivateFieldGet(this, _LiveObject_map, "f").get(key); | ||
if (oldValue !== undefined) { | ||
reverseUpdate.data[key] = oldValue; | ||
} | ||
else if (oldValue === undefined) { | ||
reverse.push({ type: live_1.OpType.DeleteObjectKey, id: this._id, key }); | ||
} | ||
} | ||
for (const key in op.data) { | ||
if (op.opId == null) { | ||
op.opId = (_a = this._doc) === null || _a === void 0 ? void 0 : _a.generateOpId(); | ||
} | ||
const lastOpId = __classPrivateFieldGet(this, _LiveObject_propToLastUpdate, "f").get(key); | ||
if (lastOpId === op.opId) { | ||
__classPrivateFieldGet(this, _LiveObject_propToLastUpdate, "f").delete(key); | ||
continue; | ||
} | ||
@@ -445,17 +530,21 @@ else if (lastOpId != null) { | ||
} | ||
this.notify(); | ||
this._notify(); | ||
return reverse; | ||
} | ||
else if (op.type === live_1.OpType.DeleteObjectKey) { | ||
const key = op.key; | ||
const oldValue = __classPrivateFieldGet(this, _LiveObject_map, "f").get(key); | ||
if (isCrdt(oldValue)) { | ||
oldValue._detach(); | ||
} | ||
__classPrivateFieldGet(this, _LiveObject_map, "f").delete(key); | ||
this.notify(); | ||
return __classPrivateFieldGet(this, _LiveObject_instances, "m", _LiveObject_applyDeleteObjectKey).call(this, op); | ||
} | ||
return super._apply(op); | ||
} | ||
/** | ||
* Transform the LiveObject into a javascript object | ||
*/ | ||
toObject() { | ||
return Object.fromEntries(__classPrivateFieldGet(this, _LiveObject_map, "f")); | ||
} | ||
/** | ||
* Adds or updates a property with a specified key and a value. | ||
* @param key The key of the property to add | ||
* @param value The value of the property to add | ||
*/ | ||
set(key, value) { | ||
@@ -465,20 +554,18 @@ // TODO: Find out why typescript complains | ||
} | ||
/** | ||
* Returns a specified property from the LiveObject. | ||
* @param key The key of the property to get | ||
*/ | ||
get(key) { | ||
return __classPrivateFieldGet(this, _LiveObject_map, "f").get(key); | ||
} | ||
/** | ||
* Adds or updates multiple properties at once with an object. | ||
* @param overrides The object used to overrides properties | ||
*/ | ||
update(overrides) { | ||
if (this._doc && this._id) { | ||
const ops = []; | ||
const opId = this._doc.generateOpId(); | ||
const updateOp = { | ||
opId, | ||
id: this._id, | ||
type: live_1.OpType.UpdateObject, | ||
data: {}, | ||
}; | ||
ops.push(updateOp); | ||
if (this._doc == null || this._id == null) { | ||
for (const key in overrides) { | ||
__classPrivateFieldGet(this, _LiveObject_propToLastUpdate, "f").set(key, opId); | ||
const oldValue = __classPrivateFieldGet(this, _LiveObject_map, "f").get(key); | ||
if (oldValue instanceof LiveObject) { | ||
if (oldValue instanceof AbstractCrdt) { | ||
oldValue._detach(); | ||
@@ -488,31 +575,81 @@ } | ||
if (newValue instanceof AbstractCrdt) { | ||
newValue._setParent(this); | ||
newValue._attach(this._doc.generateId(), this._doc); | ||
ops.push(...newValue._serialize(this._id, key)); | ||
newValue._setParentLink(this, key); | ||
} | ||
else { | ||
updateOp.data[key] = newValue; | ||
} | ||
__classPrivateFieldGet(this, _LiveObject_map, "f").set(key, newValue); | ||
} | ||
this._doc.dispatch(ops); | ||
this.notify(); | ||
this._notify(); | ||
return; | ||
} | ||
const ops = []; | ||
const reverseOps = []; | ||
const opId = this._doc.generateOpId(); | ||
const updateOp = { | ||
opId, | ||
id: this._id, | ||
type: live_1.OpType.UpdateObject, | ||
data: {}, | ||
}; | ||
ops.push(updateOp); | ||
const reverseUpdateOp = { | ||
id: this._id, | ||
type: live_1.OpType.UpdateObject, | ||
data: {}, | ||
}; | ||
reverseOps.push(reverseUpdateOp); | ||
for (const key in overrides) { | ||
__classPrivateFieldGet(this, _LiveObject_propToLastUpdate, "f").set(key, opId); | ||
const oldValue = __classPrivateFieldGet(this, _LiveObject_map, "f").get(key); | ||
if (oldValue instanceof AbstractCrdt) { | ||
reverseOps.push(...oldValue._serialize(this._id, key)); | ||
oldValue._detach(); | ||
} | ||
else if (oldValue === undefined) { | ||
reverseOps.push({ type: live_1.OpType.DeleteObjectKey, id: this._id, key }); | ||
} | ||
else { | ||
reverseUpdateOp.data[key] = oldValue; | ||
} | ||
const newValue = overrides[key]; | ||
if (newValue instanceof AbstractCrdt) { | ||
newValue._setParent(this); | ||
newValue._setParentLink(this, key); | ||
newValue._attach(this._doc.generateId(), this._doc); | ||
ops.push(...newValue._serialize(this._id, key)); | ||
} | ||
else { | ||
updateOp.data[key] = newValue; | ||
} | ||
__classPrivateFieldGet(this, _LiveObject_map, "f").set(key, newValue); | ||
} | ||
this.notify(); | ||
this._doc.addToUndoStack(reverseOps); | ||
this._doc.dispatch(ops); | ||
this._notify(); | ||
} | ||
} | ||
exports.LiveObject = LiveObject; | ||
_LiveObject_map = new WeakMap(), _LiveObject_propToLastUpdate = new WeakMap(); | ||
_LiveObject_map = new WeakMap(), _LiveObject_propToLastUpdate = new WeakMap(), _LiveObject_instances = new WeakSet(), _LiveObject_applyDeleteObjectKey = function _LiveObject_applyDeleteObjectKey(op) { | ||
const key = op.key; | ||
const oldValue = __classPrivateFieldGet(this, _LiveObject_map, "f").get(key); | ||
let result = []; | ||
if (isCrdt(oldValue)) { | ||
result = oldValue._serialize(this._id, op.key); | ||
oldValue._detach(); | ||
} | ||
else if (oldValue !== undefined) { | ||
result = [ | ||
{ | ||
type: live_1.OpType.UpdateObject, | ||
id: this._id, | ||
data: { [key]: oldValue }, | ||
}, | ||
]; | ||
} | ||
__classPrivateFieldGet(this, _LiveObject_map, "f").delete(key); | ||
this._notify(); | ||
return result; | ||
}; | ||
/** | ||
* The LiveMap class is similar to a JavaScript Map that is synchronized on all clients. | ||
* Keys should be a string, and values should be serializable to JSON. | ||
* If multiple clients update the same property simultaneously, the last modification received by the Liveblocks servers is the winner. | ||
*/ | ||
class LiveMap extends AbstractCrdt { | ||
@@ -526,3 +663,3 @@ constructor(entries) { | ||
const value = selfOrRegister(entry[1]); | ||
value._setParent(this); | ||
value._setParentLink(this, entry[0]); | ||
mappedEntries.push([entry[0], value]); | ||
@@ -578,3 +715,3 @@ } | ||
const child = deserialize(entry, parentToChildren, doc); | ||
child._setParent(map); | ||
child._setParentLink(map, crdt.parentKey); | ||
__classPrivateFieldGet(map, _LiveMap_map, "f").set(crdt.parentKey, child); | ||
@@ -603,9 +740,15 @@ } | ||
const previousValue = __classPrivateFieldGet(this, _LiveMap_map, "f").get(key); | ||
let result; | ||
if (previousValue) { | ||
result = previousValue._serialize(this._id, key); | ||
previousValue._detach(); | ||
} | ||
child._setParent(this); | ||
else { | ||
result = [{ type: live_1.OpType.DeleteCrdt, id }]; | ||
} | ||
child._setParentLink(this, key); | ||
child._attach(id, this._doc); | ||
__classPrivateFieldGet(this, _LiveMap_map, "f").set(key, child); | ||
this.notify(); | ||
this._notify(); | ||
return result; | ||
} | ||
@@ -631,4 +774,9 @@ /** | ||
child._detach(); | ||
this.notify(); | ||
this._notify(); | ||
} | ||
/** | ||
* Returns a specified element from the LiveMap. | ||
* @param key The key of the element to return. | ||
* @returns The element associated with the specified key, or undefined if the key can't be found in the LiveMap. | ||
*/ | ||
get(key) { | ||
@@ -641,2 +789,7 @@ const value = __classPrivateFieldGet(this, _LiveMap_map, "f").get(key); | ||
} | ||
/** | ||
* Adds or updates an element with a specified key and a value. | ||
* @param key The key of the element to add. Should be a string. | ||
* @param value The value of the element to add. Should be serializable to JSON. | ||
*/ | ||
set(key, value) { | ||
@@ -648,17 +801,32 @@ const oldValue = __classPrivateFieldGet(this, _LiveMap_map, "f").get(key); | ||
const item = selfOrRegister(value); | ||
item._setParent(this); | ||
item._setParentLink(this, key); | ||
__classPrivateFieldGet(this, _LiveMap_map, "f").set(key, item); | ||
if (this._doc && this._id) { | ||
item._attach(this._doc.generateId(), this._doc); | ||
const ops = item._serialize(this._id, key); | ||
this._doc.dispatch(ops); | ||
const id = this._doc.generateId(); | ||
item._attach(id, this._doc); | ||
this._doc.addToUndoStack(oldValue | ||
? oldValue._serialize(this._id, key) | ||
: [{ type: live_1.OpType.DeleteCrdt, id }]); | ||
this._doc.dispatch(item._serialize(this._id, key)); | ||
} | ||
this.notify(); | ||
this._notify(); | ||
} | ||
/** | ||
* Returns the number of elements in the LiveMap. | ||
*/ | ||
get size() { | ||
return __classPrivateFieldGet(this, _LiveMap_map, "f").size; | ||
} | ||
/** | ||
* Returns a boolean indicating whether an element with the specified key exists or not. | ||
* @param key The key of the element to test for presence. | ||
*/ | ||
has(key) { | ||
return __classPrivateFieldGet(this, _LiveMap_map, "f").has(key); | ||
} | ||
/** | ||
* Removes the specified element by key. | ||
* @param key The key of the element to remove. | ||
* @returns true if an element existed and has been removed, or false if the element does not exist. | ||
*/ | ||
delete(key) { | ||
@@ -671,8 +839,12 @@ const item = __classPrivateFieldGet(this, _LiveMap_map, "f").get(key); | ||
if (this._doc && item._id) { | ||
this._doc.addToUndoStack(item._serialize(this._id, key)); | ||
this._doc.dispatch([{ type: live_1.OpType.DeleteCrdt, id: item._id }]); | ||
} | ||
__classPrivateFieldGet(this, _LiveMap_map, "f").delete(key); | ||
this.notify(); | ||
this._notify(); | ||
return true; | ||
} | ||
/** | ||
* Returns a new Iterator object that contains the [key, value] pairs for each element. | ||
*/ | ||
entries() { | ||
@@ -699,8 +871,17 @@ const innerIterator = __classPrivateFieldGet(this, _LiveMap_map, "f").entries(); | ||
} | ||
/** | ||
* Same function object as the initial value of the entries method. | ||
*/ | ||
[(_LiveMap_map = new WeakMap(), Symbol.iterator)]() { | ||
return this.entries(); | ||
} | ||
/** | ||
* Returns a new Iterator object that contains the keys for each element. | ||
*/ | ||
keys() { | ||
return __classPrivateFieldGet(this, _LiveMap_map, "f").keys(); | ||
} | ||
/** | ||
* Returns a new Iterator object that contains the values for each element. | ||
*/ | ||
values() { | ||
@@ -726,2 +907,6 @@ const innerIterator = __classPrivateFieldGet(this, _LiveMap_map, "f").values(); | ||
} | ||
/** | ||
* Executes a provided function once per each key/value pair in the Map object, in insertion order. | ||
* @param callback Function to execute for each entry in the map. | ||
*/ | ||
forEach(callback) { | ||
@@ -734,2 +919,5 @@ for (const entry of this) { | ||
exports.LiveMap = LiveMap; | ||
/** | ||
* INTERNAL | ||
*/ | ||
class LiveRegister extends AbstractCrdt { | ||
@@ -778,4 +966,10 @@ constructor(data) { | ||
} | ||
_apply(op) { | ||
return super._apply(op); | ||
} | ||
} | ||
_LiveRegister_data = new WeakMap(); | ||
/** | ||
* The LiveList class represents an ordered collection of items that is synchorinized across clients. | ||
*/ | ||
class LiveList extends AbstractCrdt { | ||
@@ -806,3 +1000,3 @@ constructor(items = []) { | ||
const child = deserialize(entry, parentToChildren, doc); | ||
child._setParent(list); | ||
child._setParentLink(list, entry[1].parentKey); | ||
__classPrivateFieldGet(list, _LiveList_items, "f").push([child, entry[1].parentKey]); | ||
@@ -863,3 +1057,3 @@ __classPrivateFieldGet(list, _LiveList_items, "f").sort((itemA, itemB) => (0, position_1.compare)(itemA[1], itemB[1])); | ||
child._attach(id, this._doc); | ||
child._setParent(this); | ||
child._setParentLink(this, key); | ||
const index = __classPrivateFieldGet(this, _LiveList_items, "f").findIndex((entry) => entry[1] === key); | ||
@@ -872,3 +1066,4 @@ // Assign a temporary position until we get the fix from the backend | ||
__classPrivateFieldGet(this, _LiveList_items, "f").sort((itemA, itemB) => (0, position_1.compare)(itemA[1], itemB[1])); | ||
this.notify(); | ||
this._notify(); | ||
return [{ type: live_1.OpType.DeleteCrdt, id }]; | ||
} | ||
@@ -884,3 +1079,3 @@ /** | ||
} | ||
this.notify(); | ||
this._notify(); | ||
} | ||
@@ -892,2 +1087,3 @@ /** | ||
var _a; | ||
child._setParentLink(this, key); | ||
const index = __classPrivateFieldGet(this, _LiveList_items, "f").findIndex((entry) => entry[1] === key); | ||
@@ -903,21 +1099,29 @@ // Assign a temporary position until we get the fix from the backend | ||
__classPrivateFieldGet(this, _LiveList_items, "f").sort((itemA, itemB) => (0, position_1.compare)(itemA[1], itemB[1])); | ||
this.notify(); | ||
this._notify(); | ||
} | ||
/** | ||
* INTERNAL | ||
*/ | ||
_apply(op) { | ||
return super._apply(op); | ||
} | ||
/** | ||
* Returns the number of elements. | ||
*/ | ||
get length() { | ||
return __classPrivateFieldGet(this, _LiveList_items, "f").length; | ||
} | ||
push(item) { | ||
const position = __classPrivateFieldGet(this, _LiveList_items, "f").length === 0 | ||
? (0, position_1.makePosition)() | ||
: (0, position_1.makePosition)(__classPrivateFieldGet(this, _LiveList_items, "f")[__classPrivateFieldGet(this, _LiveList_items, "f").length - 1][1]); | ||
const value = selfOrRegister(item); | ||
value._setParent(this); | ||
__classPrivateFieldGet(this, _LiveList_items, "f").push([value, position]); | ||
this.notify(); | ||
if (this._doc && this._id) { | ||
value._attach(this._doc.generateId(), this._doc); | ||
this._doc.dispatch(value._serialize(this._id, position)); | ||
} | ||
/** | ||
* Adds one element to the end of the LiveList. | ||
* @param element The element to add to the end of the LiveList. | ||
*/ | ||
push(element) { | ||
return this.insert(element, this.length); | ||
} | ||
insert(item, index) { | ||
/** | ||
* Inserts one element at a specified index. | ||
* @param element The element to insert. | ||
* @param index The index at which you want to insert the element. | ||
*/ | ||
insert(element, index) { | ||
if (index < 0 || index > __classPrivateFieldGet(this, _LiveList_items, "f").length) { | ||
@@ -929,12 +1133,19 @@ throw new Error(`Cannot delete list item at index "${index}". index should be between 0 and ${__classPrivateFieldGet(this, _LiveList_items, "f").length}`); | ||
const position = (0, position_1.makePosition)(before, after); | ||
const value = selfOrRegister(item); | ||
value._setParent(this); | ||
const value = selfOrRegister(element); | ||
value._setParentLink(this, position); | ||
__classPrivateFieldGet(this, _LiveList_items, "f").push([value, position]); | ||
__classPrivateFieldGet(this, _LiveList_items, "f").sort((itemA, itemB) => (0, position_1.compare)(itemA[1], itemB[1])); | ||
this.notify(); | ||
this._notify(); | ||
if (this._doc && this._id) { | ||
value._attach(this._doc.generateId(), this._doc); | ||
const id = this._doc.generateId(); | ||
value._attach(id, this._doc); | ||
this._doc.addToUndoStack([{ type: live_1.OpType.DeleteCrdt, id }]); | ||
this._doc.dispatch(value._serialize(this._id, position)); | ||
} | ||
} | ||
/** | ||
* Move one element from one index to another. | ||
* @param index The index of the element to move | ||
* @param targetIndex The index where the element should be after moving. | ||
*/ | ||
move(index, targetIndex) { | ||
@@ -969,6 +1180,15 @@ if (targetIndex < 0) { | ||
const item = __classPrivateFieldGet(this, _LiveList_items, "f")[index]; | ||
const previousPosition = item[1]; | ||
item[1] = position; | ||
item[0]._setParentLink(this, position); | ||
__classPrivateFieldGet(this, _LiveList_items, "f").sort((itemA, itemB) => (0, position_1.compare)(itemA[1], itemB[1])); | ||
this.notify(); | ||
this._notify(); | ||
if (this._doc && this._id) { | ||
this._doc.addToUndoStack([ | ||
{ | ||
type: live_1.OpType.SetParentKey, | ||
id: item[0]._id, | ||
parentKey: previousPosition, | ||
}, | ||
]); | ||
this._doc.dispatch([ | ||
@@ -983,2 +1203,6 @@ { | ||
} | ||
/** | ||
* Deletes an element at the specified index | ||
* @param index The index of the element to delete | ||
*/ | ||
delete(index) { | ||
@@ -994,2 +1218,3 @@ if (index < 0 || index >= __classPrivateFieldGet(this, _LiveList_items, "f").length) { | ||
if (childRecordId) { | ||
this._doc.addToUndoStack(item[0]._serialize(this._id, item[1])); | ||
this._doc.dispatch([ | ||
@@ -1003,22 +1228,54 @@ { | ||
} | ||
this.notify(); | ||
this._notify(); | ||
} | ||
/** | ||
* Returns an Array of all the elements in the LiveList. | ||
*/ | ||
toArray() { | ||
return __classPrivateFieldGet(this, _LiveList_items, "f").map((entry) => selfOrRegisterValue(entry[0])); | ||
} | ||
/** | ||
* Tests whether all elements pass the test implemented by the provided function. | ||
* @param predicate Function to test for each element, taking two arguments (the element and its index). | ||
* @returns true if the predicate function returns a truthy value for every element. Otherwise, false. | ||
*/ | ||
every(predicate) { | ||
return this.toArray().every(predicate); | ||
} | ||
/** | ||
* Creates an array with all elements that pass the test implemented by the provided function. | ||
* @param predicate Function to test each element of the LiveList. Return a value that coerces to true to keep the element, or to false otherwise. | ||
* @returns An array with the elements that pass the test. | ||
*/ | ||
filter(predicate) { | ||
return this.toArray().filter(predicate); | ||
} | ||
/** | ||
* Returns the first element that satisfies the provided testing function. | ||
* @param predicate Function to execute on each value. | ||
* @returns The value of the first element in the LiveList that satisfies the provided testing function. Otherwise, undefined is returned. | ||
*/ | ||
find(predicate) { | ||
return this.toArray().find(predicate); | ||
} | ||
/** | ||
* Returns the index of the first element in the LiveList that satisfies the provided testing function. | ||
* @param predicate Function to execute on each value until the function returns true, indicating that the satisfying element was found. | ||
* @returns The index of the first element in the LiveList that passes the test. Otherwise, -1. | ||
*/ | ||
findIndex(predicate) { | ||
return this.toArray().findIndex(predicate); | ||
} | ||
/** | ||
* Executes a provided function once for each element. | ||
* @param callbackfn Function to execute on each element. | ||
*/ | ||
forEach(callbackfn) { | ||
return this.toArray().forEach(callbackfn); | ||
} | ||
/** | ||
* Get the element at the specified index. | ||
* @param index The index on the element to get. | ||
* @returns The element at the specified index or undefined. | ||
*/ | ||
get(index) { | ||
@@ -1030,11 +1287,33 @@ if (index < 0 || index >= __classPrivateFieldGet(this, _LiveList_items, "f").length) { | ||
} | ||
/** | ||
* Returns the first index at which a given element can be found in the LiveList, or -1 if it is not present. | ||
* @param searchElement Element to locate. | ||
* @param fromIndex The index to start the search at. | ||
* @returns The first index of the element in the LiveList; -1 if not found. | ||
*/ | ||
indexOf(searchElement, fromIndex) { | ||
return this.toArray().indexOf(searchElement, fromIndex); | ||
} | ||
/** | ||
* Returns the last index at which a given element can be found in the LiveList, or -1 if it is not present. The LiveLsit is searched backwards, starting at fromIndex. | ||
* @param searchElement Element to locate. | ||
* @param fromIndex The index at which to start searching backwards. | ||
* @returns | ||
*/ | ||
lastIndexOf(searchElement, fromIndex) { | ||
return this.toArray().lastIndexOf(searchElement, fromIndex); | ||
} | ||
/** | ||
* Creates an array populated with the results of calling a provided function on every element. | ||
* @param callback Function that is called for every element. | ||
* @returns An array with each element being the result of the callback function. | ||
*/ | ||
map(callback) { | ||
return __classPrivateFieldGet(this, _LiveList_items, "f").map((entry, i) => callback(selfOrRegisterValue(entry[0]), i)); | ||
} | ||
/** | ||
* Tests whether at least one element in the LiveList passes the test implemented by the provided function. | ||
* @param predicate Function to test for each element. | ||
* @returns true if the callback function returns a truthy value for at least one element. Otherwise, false. | ||
*/ | ||
some(predicate) { | ||
@@ -1041,0 +1320,0 @@ return this.toArray().some(predicate); |
@@ -117,3 +117,3 @@ import { Presence } from "./types"; | ||
export declare type UpdateObjectOp = { | ||
opId: string; | ||
opId?: string; | ||
id: string; | ||
@@ -120,0 +120,0 @@ type: OpType.UpdateObject; |
@@ -84,2 +84,4 @@ import { Others, Presence, ClientOptions, Room, MyPresenceCallback, OthersEventCallback, AuthEndpoint, EventCallback, User, Connection, ErrorCallback, AuthenticationToken, ConnectionCallback } from "./types"; | ||
broadcastEvent: (event: any) => void; | ||
undo: () => void; | ||
redo: () => void; | ||
getStorage: <TRoot>() => Promise<{ | ||
@@ -86,0 +88,0 @@ root: import("./doc").LiveObject<TRoot>; |
@@ -503,2 +503,8 @@ "use strict"; | ||
} | ||
function undo() { | ||
storage.undo(); | ||
} | ||
function redo() { | ||
storage.redo(); | ||
} | ||
return { | ||
@@ -522,2 +528,4 @@ // Internal | ||
broadcastEvent, | ||
undo, | ||
redo, | ||
getStorage, | ||
@@ -605,2 +613,4 @@ selectors: { | ||
getStorage: machine.getStorage, | ||
undo: machine.undo, | ||
redo: machine.redo, | ||
}; | ||
@@ -607,0 +617,0 @@ return { |
@@ -19,2 +19,4 @@ import { Doc as Doc } from "./doc"; | ||
onMessage(message: ServerMessage): Promise<void>; | ||
undo(): void; | ||
redo(): void; | ||
} |
@@ -53,5 +53,3 @@ "use strict"; | ||
case live_1.ServerMessageType.UpdateStorage: { | ||
for (const op of message.ops) { | ||
(_b = this._doc) === null || _b === void 0 ? void 0 : _b.apply(op); | ||
} | ||
(_b = this._doc) === null || _b === void 0 ? void 0 : _b.apply(message.ops); | ||
break; | ||
@@ -62,3 +60,11 @@ } | ||
} | ||
undo() { | ||
var _a; | ||
(_a = this._doc) === null || _a === void 0 ? void 0 : _a.undo(); | ||
} | ||
redo() { | ||
var _a; | ||
(_a = this._doc) === null || _a === void 0 ? void 0 : _a.redo(); | ||
} | ||
} | ||
exports.default = Storage; |
@@ -269,3 +269,5 @@ import { LiveObject } from "./doc"; | ||
}>; | ||
undo: () => void; | ||
redo: () => void; | ||
}; | ||
export {}; |
@@ -12,4 +12,7 @@ import { Op, SerializedCrdtWithId, SerializedList } from "./live"; | ||
getItem(id: string): AbstractCrdt | undefined; | ||
apply(op: Op): void; | ||
apply(ops: Op[]): Op[]; | ||
get root(): LiveObject<T>; | ||
addToUndoStack(ops: Op[]): void; | ||
undo(): void; | ||
redo(): void; | ||
count(): number; | ||
@@ -36,8 +39,13 @@ generateId(): string; | ||
*/ | ||
_setParent(parent: AbstractCrdt): void; | ||
get _parentKey(): string | undefined; | ||
_apply(op: Op): Op[]; | ||
/** | ||
* INTERNAL | ||
*/ | ||
_setParentLink(parent: AbstractCrdt, key: string): void; | ||
/** | ||
* INTERNAL | ||
*/ | ||
_attach(id: string, doc: Doc): void; | ||
abstract _attachChild(id: string, key: string, crdt: AbstractCrdt): void; | ||
abstract _attachChild(id: string, key: string, crdt: AbstractCrdt): Op[]; | ||
/** | ||
@@ -48,9 +56,29 @@ * INTERNAL | ||
abstract _detachChild(crdt: AbstractCrdt): void; | ||
/** | ||
* Subscribes to updates. | ||
*/ | ||
subscribe(listener: () => void): void; | ||
/** | ||
* Subscribes to updates and children updates. | ||
*/ | ||
subscribeDeep(listener: () => void): void; | ||
/** | ||
* Unsubscribes to updates. | ||
*/ | ||
unsubscribe(listener: () => void): void; | ||
/** | ||
* Unsubscribes to updates and children updates. | ||
*/ | ||
unsubscribeDeep(listener: () => void): void; | ||
notify(onlyDeep?: boolean): void; | ||
/** | ||
* INTERNAL | ||
*/ | ||
_notify(onlyDeep?: boolean): void; | ||
abstract _serialize(parentId: string, parentKey: string): Op[]; | ||
} | ||
/** | ||
* The LiveObject class is similar to a JavaScript object that is synchronized on all clients. | ||
* Keys should be a string, and values should be serializable to JSON. | ||
* If multiple clients update the same property simultaneously, the last modification received by the Liveblocks servers is the winner. | ||
*/ | ||
export declare class LiveObject<T extends Record<string, any> = Record<string, any>> extends AbstractCrdt { | ||
@@ -76,3 +104,3 @@ #private; | ||
*/ | ||
_attachChild(id: string, key: keyof T, child: AbstractCrdt): void; | ||
_attachChild(id: string, key: keyof T, child: AbstractCrdt): Op[]; | ||
/** | ||
@@ -89,8 +117,29 @@ * INTERNAL | ||
*/ | ||
_apply(op: Op): void; | ||
_apply(op: Op): Op[]; | ||
/** | ||
* Transform the LiveObject into a javascript object | ||
*/ | ||
toObject(): T; | ||
/** | ||
* Adds or updates a property with a specified key and a value. | ||
* @param key The key of the property to add | ||
* @param value The value of the property to add | ||
*/ | ||
set<TKey extends keyof T>(key: TKey, value: T[TKey]): void; | ||
/** | ||
* Returns a specified property from the LiveObject. | ||
* @param key The key of the property to get | ||
*/ | ||
get<TKey extends keyof T>(key: TKey): T[TKey]; | ||
/** | ||
* Adds or updates multiple properties at once with an object. | ||
* @param overrides The object used to overrides properties | ||
*/ | ||
update(overrides: Partial<T>): void; | ||
} | ||
/** | ||
* The LiveMap class is similar to a JavaScript Map that is synchronized on all clients. | ||
* Keys should be a string, and values should be serializable to JSON. | ||
* If multiple clients update the same property simultaneously, the last modification received by the Liveblocks servers is the winner. | ||
*/ | ||
export declare class LiveMap<TKey extends string, TValue> extends AbstractCrdt { | ||
@@ -114,3 +163,3 @@ #private; | ||
*/ | ||
_attachChild(id: string, key: TKey, child: AbstractCrdt): void; | ||
_attachChild(id: string, key: TKey, child: AbstractCrdt): Op[]; | ||
/** | ||
@@ -124,13 +173,54 @@ * INTERNAL | ||
_detachChild(child: AbstractCrdt): void; | ||
/** | ||
* Returns a specified element from the LiveMap. | ||
* @param key The key of the element to return. | ||
* @returns The element associated with the specified key, or undefined if the key can't be found in the LiveMap. | ||
*/ | ||
get(key: TKey): TValue | undefined; | ||
/** | ||
* Adds or updates an element with a specified key and a value. | ||
* @param key The key of the element to add. Should be a string. | ||
* @param value The value of the element to add. Should be serializable to JSON. | ||
*/ | ||
set(key: TKey, value: TValue): void; | ||
/** | ||
* Returns the number of elements in the LiveMap. | ||
*/ | ||
get size(): number; | ||
/** | ||
* Returns a boolean indicating whether an element with the specified key exists or not. | ||
* @param key The key of the element to test for presence. | ||
*/ | ||
has(key: TKey): boolean; | ||
/** | ||
* Removes the specified element by key. | ||
* @param key The key of the element to remove. | ||
* @returns true if an element existed and has been removed, or false if the element does not exist. | ||
*/ | ||
delete(key: TKey): boolean; | ||
/** | ||
* Returns a new Iterator object that contains the [key, value] pairs for each element. | ||
*/ | ||
entries(): IterableIterator<[string, TValue]>; | ||
/** | ||
* Same function object as the initial value of the entries method. | ||
*/ | ||
[Symbol.iterator](): IterableIterator<[string, TValue]>; | ||
/** | ||
* Returns a new Iterator object that contains the keys for each element. | ||
*/ | ||
keys(): IterableIterator<TKey>; | ||
/** | ||
* Returns a new Iterator object that contains the values for each element. | ||
*/ | ||
values(): IterableIterator<TValue>; | ||
/** | ||
* Executes a provided function once per each key/value pair in the Map object, in insertion order. | ||
* @param callback Function to execute for each entry in the map. | ||
*/ | ||
forEach(callback: (value: TValue, key: TKey, map: LiveMap<TKey, TValue>) => void): void; | ||
} | ||
/** | ||
* The LiveList class represents an ordered collection of items that is synchorinized across clients. | ||
*/ | ||
export declare class LiveList<T> extends AbstractCrdt { | ||
@@ -158,3 +248,3 @@ #private; | ||
*/ | ||
_attachChild(id: string, key: string, child: AbstractCrdt): void; | ||
_attachChild(id: string, key: string, child: AbstractCrdt): Op[]; | ||
/** | ||
@@ -168,17 +258,96 @@ * INTERNAL | ||
_setChildKey(key: string, child: AbstractCrdt): void; | ||
/** | ||
* INTERNAL | ||
*/ | ||
_apply(op: Op): Op[]; | ||
/** | ||
* Returns the number of elements. | ||
*/ | ||
get length(): number; | ||
push(item: T): void; | ||
insert(item: T, index: number): void; | ||
/** | ||
* Adds one element to the end of the LiveList. | ||
* @param element The element to add to the end of the LiveList. | ||
*/ | ||
push(element: T): void; | ||
/** | ||
* Inserts one element at a specified index. | ||
* @param element The element to insert. | ||
* @param index The index at which you want to insert the element. | ||
*/ | ||
insert(element: T, index: number): void; | ||
/** | ||
* Move one element from one index to another. | ||
* @param index The index of the element to move | ||
* @param targetIndex The index where the element should be after moving. | ||
*/ | ||
move(index: number, targetIndex: number): void; | ||
/** | ||
* Deletes an element at the specified index | ||
* @param index The index of the element to delete | ||
*/ | ||
delete(index: number): void; | ||
/** | ||
* Returns an Array of all the elements in the LiveList. | ||
*/ | ||
toArray(): T[]; | ||
/** | ||
* Tests whether all elements pass the test implemented by the provided function. | ||
* @param predicate Function to test for each element, taking two arguments (the element and its index). | ||
* @returns true if the predicate function returns a truthy value for every element. Otherwise, false. | ||
*/ | ||
every(predicate: (value: T, index: number) => unknown): boolean; | ||
/** | ||
* Creates an array with all elements that pass the test implemented by the provided function. | ||
* @param predicate Function to test each element of the LiveList. Return a value that coerces to true to keep the element, or to false otherwise. | ||
* @returns An array with the elements that pass the test. | ||
*/ | ||
filter(predicate: (value: T, index: number) => unknown): T[]; | ||
/** | ||
* Returns the first element that satisfies the provided testing function. | ||
* @param predicate Function to execute on each value. | ||
* @returns The value of the first element in the LiveList that satisfies the provided testing function. Otherwise, undefined is returned. | ||
*/ | ||
find(predicate: (value: T, index: number) => unknown): T | undefined; | ||
/** | ||
* Returns the index of the first element in the LiveList that satisfies the provided testing function. | ||
* @param predicate Function to execute on each value until the function returns true, indicating that the satisfying element was found. | ||
* @returns The index of the first element in the LiveList that passes the test. Otherwise, -1. | ||
*/ | ||
findIndex(predicate: (value: T, index: number) => unknown): number; | ||
/** | ||
* Executes a provided function once for each element. | ||
* @param callbackfn Function to execute on each element. | ||
*/ | ||
forEach(callbackfn: (value: T, index: number) => void): void; | ||
/** | ||
* Get the element at the specified index. | ||
* @param index The index on the element to get. | ||
* @returns The element at the specified index or undefined. | ||
*/ | ||
get(index: number): T | undefined; | ||
/** | ||
* Returns the first index at which a given element can be found in the LiveList, or -1 if it is not present. | ||
* @param searchElement Element to locate. | ||
* @param fromIndex The index to start the search at. | ||
* @returns The first index of the element in the LiveList; -1 if not found. | ||
*/ | ||
indexOf(searchElement: T, fromIndex?: number): number; | ||
/** | ||
* Returns the last index at which a given element can be found in the LiveList, or -1 if it is not present. The LiveLsit is searched backwards, starting at fromIndex. | ||
* @param searchElement Element to locate. | ||
* @param fromIndex The index at which to start searching backwards. | ||
* @returns | ||
*/ | ||
lastIndexOf(searchElement: T, fromIndex?: number): number; | ||
/** | ||
* Creates an array populated with the results of calling a provided function on every element. | ||
* @param callback Function that is called for every element. | ||
* @returns An array with each element being the result of the callback function. | ||
*/ | ||
map<U>(callback: (value: T, index: number) => U): U[]; | ||
/** | ||
* Tests whether at least one element in the LiveList passes the test implemented by the provided function. | ||
* @param predicate Function to test for each element. | ||
* @returns true if the callback function returns a truthy value for at least one element. Otherwise, false. | ||
*/ | ||
some(predicate: (value: T, index: number) => unknown): boolean; | ||
@@ -185,0 +354,0 @@ [Symbol.iterator](): IterableIterator<T>; |
@@ -12,3 +12,3 @@ var __classPrivateFieldSet = (this && this.__classPrivateFieldSet) || function (receiver, state, value, kind, f) { | ||
}; | ||
var _Doc_instances, _Doc_clock, _Doc_opClock, _Doc_items, _Doc_root, _Doc_actor, _Doc_dispatch, _Doc_applyCreateRegister, _Doc_applyDeleteRecordKey, _Doc_applyUpdateRecord, _Doc_applyCreateMap, _Doc_applyCreateList, _Doc_applyCreateObject, _Doc_applyDeleteRecord, _Doc_applySetParentKey, _AbstractCrdt_listeners, _AbstractCrdt_deepListeners, _AbstractCrdt_parent, _AbstractCrdt_doc, _AbstractCrdt_id, _LiveObject_map, _LiveObject_propToLastUpdate, _LiveMap_map, _LiveRegister_data, _LiveList_items, _LiveListIterator_innerIterator; | ||
var _Doc_instances, _Doc_clock, _Doc_opClock, _Doc_items, _Doc_root, _Doc_actor, _Doc_dispatch, _Doc_undoStack, _Doc_redoStack, _Doc_applyOp, _AbstractCrdt_instances, _AbstractCrdt_listeners, _AbstractCrdt_deepListeners, _AbstractCrdt_parent, _AbstractCrdt_doc, _AbstractCrdt_id, _AbstractCrdt_parentKey, _AbstractCrdt_applySetParentKey, _AbstractCrdt_applyRegister, _AbstractCrdt_applyCreateObject, _AbstractCrdt_applyCreateMap, _AbstractCrdt_applyCreateList, _LiveObject_instances, _LiveObject_map, _LiveObject_propToLastUpdate, _LiveObject_applyDeleteObjectKey, _LiveMap_map, _LiveRegister_data, _LiveList_items, _LiveListIterator_innerIterator; | ||
import { remove } from "./utils"; | ||
@@ -18,2 +18,3 @@ import { CrdtType, OpType, } from "./live"; | ||
function noOp() { } | ||
const MAX_UNDO_STACK = 50; | ||
export class Doc { | ||
@@ -28,2 +29,4 @@ constructor(root, actor = 0, dispatch = noOp) { | ||
_Doc_dispatch.set(this, void 0); | ||
_Doc_undoStack.set(this, []); | ||
_Doc_redoStack.set(this, []); | ||
__classPrivateFieldSet(this, _Doc_root, root, "f"); | ||
@@ -69,2 +72,3 @@ __classPrivateFieldSet(this, _Doc_actor, actor, "f"); | ||
dispatch(ops) { | ||
__classPrivateFieldSet(this, _Doc_redoStack, [], "f"); | ||
__classPrivateFieldGet(this, _Doc_dispatch, "f").call(this, ops); | ||
@@ -81,37 +85,8 @@ } | ||
} | ||
apply(op) { | ||
switch (op.type) { | ||
case OpType.UpdateObject: { | ||
__classPrivateFieldGet(this, _Doc_instances, "m", _Doc_applyUpdateRecord).call(this, op); | ||
break; | ||
} | ||
case OpType.CreateObject: { | ||
__classPrivateFieldGet(this, _Doc_instances, "m", _Doc_applyCreateObject).call(this, op); | ||
break; | ||
} | ||
case OpType.CreateMap: { | ||
__classPrivateFieldGet(this, _Doc_instances, "m", _Doc_applyCreateMap).call(this, op); | ||
break; | ||
} | ||
case OpType.CreateList: { | ||
__classPrivateFieldGet(this, _Doc_instances, "m", _Doc_applyCreateList).call(this, op); | ||
break; | ||
} | ||
case OpType.DeleteCrdt: { | ||
__classPrivateFieldGet(this, _Doc_instances, "m", _Doc_applyDeleteRecord).call(this, op); | ||
break; | ||
} | ||
case OpType.SetParentKey: { | ||
__classPrivateFieldGet(this, _Doc_instances, "m", _Doc_applySetParentKey).call(this, op); | ||
break; | ||
} | ||
case OpType.DeleteObjectKey: { | ||
__classPrivateFieldGet(this, _Doc_instances, "m", _Doc_applyDeleteRecordKey).call(this, op); | ||
break; | ||
} | ||
case OpType.CreateRegister: { | ||
__classPrivateFieldGet(this, _Doc_instances, "m", _Doc_applyCreateRegister).call(this, op); | ||
break; | ||
} | ||
apply(ops) { | ||
const reverse = []; | ||
for (const op of ops) { | ||
reverse.push(...__classPrivateFieldGet(this, _Doc_instances, "m", _Doc_applyOp).call(this, op)); | ||
} | ||
return reverse; | ||
} | ||
@@ -121,2 +96,24 @@ get root() { | ||
} | ||
addToUndoStack(ops) { | ||
if (__classPrivateFieldGet(this, _Doc_undoStack, "f").length >= MAX_UNDO_STACK) { | ||
__classPrivateFieldGet(this, _Doc_undoStack, "f").shift(); | ||
} | ||
__classPrivateFieldGet(this, _Doc_undoStack, "f").push(ops); | ||
} | ||
undo() { | ||
const ops = __classPrivateFieldGet(this, _Doc_undoStack, "f").pop(); | ||
if (ops == null) { | ||
return; | ||
} | ||
__classPrivateFieldGet(this, _Doc_redoStack, "f").push(this.apply(ops)); | ||
__classPrivateFieldGet(this, _Doc_dispatch, "f").call(this, ops); | ||
} | ||
redo() { | ||
const ops = __classPrivateFieldGet(this, _Doc_redoStack, "f").pop(); | ||
if (ops == null) { | ||
return; | ||
} | ||
__classPrivateFieldGet(this, _Doc_undoStack, "f").push(this.apply(ops)); | ||
__classPrivateFieldGet(this, _Doc_dispatch, "f").call(this, ops); | ||
} | ||
count() { | ||
@@ -134,83 +131,32 @@ return __classPrivateFieldGet(this, _Doc_items, "f").size; | ||
} | ||
_Doc_clock = new WeakMap(), _Doc_opClock = new WeakMap(), _Doc_items = new WeakMap(), _Doc_root = new WeakMap(), _Doc_actor = new WeakMap(), _Doc_dispatch = new WeakMap(), _Doc_instances = new WeakSet(), _Doc_applyCreateRegister = function _Doc_applyCreateRegister(op) { | ||
if (__classPrivateFieldGet(this, _Doc_items, "f").has(op.id)) { | ||
return; | ||
} | ||
const parent = __classPrivateFieldGet(this, _Doc_items, "f").get(op.parentId); | ||
if (parent == null) { | ||
return; | ||
} | ||
if (!(parent instanceof LiveMap) && !(parent instanceof LiveList)) { | ||
throw new Error("LiveRegister can only be attached to a LiveMap or LiveList"); | ||
} | ||
const newRegister = new LiveRegister(op.data); | ||
parent._attachChild(op.id, op.parentKey, newRegister); | ||
}, _Doc_applyDeleteRecordKey = function _Doc_applyDeleteRecordKey(op) { | ||
const item = __classPrivateFieldGet(this, _Doc_items, "f").get(op.id); | ||
if (item && item instanceof LiveObject) { | ||
item._apply(op); | ||
} | ||
}, _Doc_applyUpdateRecord = function _Doc_applyUpdateRecord(op) { | ||
const item = __classPrivateFieldGet(this, _Doc_items, "f").get(op.id); | ||
if (item && item instanceof LiveObject) { | ||
item._apply(op); | ||
} | ||
}, _Doc_applyCreateMap = function _Doc_applyCreateMap(op) { | ||
if (__classPrivateFieldGet(this, _Doc_items, "f").has(op.id)) { | ||
return; | ||
} | ||
const parent = __classPrivateFieldGet(this, _Doc_items, "f").get(op.parentId); | ||
if (parent == null) { | ||
return; | ||
} | ||
const newMap = new LiveMap(); | ||
parent._attachChild(op.id, op.parentKey, newMap); | ||
}, _Doc_applyCreateList = function _Doc_applyCreateList(op) { | ||
if (__classPrivateFieldGet(this, _Doc_items, "f").has(op.id)) { | ||
return; | ||
} | ||
const parent = __classPrivateFieldGet(this, _Doc_items, "f").get(op.parentId); | ||
if (parent == null) { | ||
return; | ||
} | ||
const list = new LiveList(); | ||
parent._attachChild(op.id, op.parentKey, list); | ||
}, _Doc_applyCreateObject = function _Doc_applyCreateObject(op) { | ||
if (__classPrivateFieldGet(this, _Doc_items, "f").has(op.id)) { | ||
return; | ||
} | ||
if (op.parentId && op.parentKey) { | ||
const parent = __classPrivateFieldGet(this, _Doc_items, "f").get(op.parentId); | ||
if (parent == null) { | ||
return; | ||
_Doc_clock = new WeakMap(), _Doc_opClock = new WeakMap(), _Doc_items = new WeakMap(), _Doc_root = new WeakMap(), _Doc_actor = new WeakMap(), _Doc_dispatch = new WeakMap(), _Doc_undoStack = new WeakMap(), _Doc_redoStack = new WeakMap(), _Doc_instances = new WeakSet(), _Doc_applyOp = function _Doc_applyOp(op) { | ||
switch (op.type) { | ||
case OpType.DeleteObjectKey: | ||
case OpType.UpdateObject: | ||
case OpType.DeleteCrdt: | ||
case OpType.SetParentKey: { | ||
const item = __classPrivateFieldGet(this, _Doc_items, "f").get(op.id); | ||
if (item == null) { | ||
return []; | ||
} | ||
return item._apply(op); | ||
break; | ||
} | ||
const newObj = new LiveObject(op.data); | ||
parent._attachChild(op.id, op.parentKey, newObj); | ||
case OpType.CreateList: | ||
case OpType.CreateObject: | ||
case OpType.CreateMap: | ||
case OpType.CreateRegister: { | ||
const parent = __classPrivateFieldGet(this, _Doc_items, "f").get(op.parentId); | ||
if (parent == null) { | ||
return []; | ||
} | ||
return parent._apply(op); | ||
break; | ||
} | ||
} | ||
}, _Doc_applyDeleteRecord = function _Doc_applyDeleteRecord(op) { | ||
const item = __classPrivateFieldGet(this, _Doc_items, "f").get(op.id); | ||
if (item == null) { | ||
return; | ||
} | ||
const parent = item._parent; | ||
if (parent == null) { | ||
return; | ||
} | ||
if (parent) { | ||
parent._detachChild(item); | ||
} | ||
}, _Doc_applySetParentKey = function _Doc_applySetParentKey(op) { | ||
const item = __classPrivateFieldGet(this, _Doc_items, "f").get(op.id); | ||
if (item == null) { | ||
return; | ||
} | ||
if (item._parent == null) { | ||
return; | ||
} | ||
if (item._parent instanceof LiveList) { | ||
item._parent._setChildKey(op.parentKey, item); | ||
} | ||
return []; | ||
}; | ||
class AbstractCrdt { | ||
constructor() { | ||
_AbstractCrdt_instances.add(this); | ||
_AbstractCrdt_listeners.set(this, []); | ||
@@ -221,2 +167,3 @@ _AbstractCrdt_deepListeners.set(this, []); | ||
_AbstractCrdt_id.set(this, void 0); | ||
_AbstractCrdt_parentKey.set(this, void 0); | ||
} | ||
@@ -244,6 +191,41 @@ /** | ||
*/ | ||
_setParent(parent) { | ||
if (__classPrivateFieldGet(this, _AbstractCrdt_parent, "f")) { | ||
get _parentKey() { | ||
return __classPrivateFieldGet(this, _AbstractCrdt_parentKey, "f"); | ||
} | ||
_apply(op) { | ||
switch (op.type) { | ||
case OpType.DeleteCrdt: { | ||
if (this._parent != null && this._parentKey != null) { | ||
const reverse = this._serialize(this._parent._id, this._parentKey); | ||
this._parent._detachChild(this); | ||
return reverse; | ||
} | ||
return []; | ||
} | ||
case OpType.CreateObject: { | ||
return __classPrivateFieldGet(this, _AbstractCrdt_instances, "m", _AbstractCrdt_applyCreateObject).call(this, op); | ||
} | ||
case OpType.CreateMap: { | ||
return __classPrivateFieldGet(this, _AbstractCrdt_instances, "m", _AbstractCrdt_applyCreateMap).call(this, op); | ||
} | ||
case OpType.CreateRegister: { | ||
return __classPrivateFieldGet(this, _AbstractCrdt_instances, "m", _AbstractCrdt_applyRegister).call(this, op); | ||
} | ||
case OpType.CreateList: { | ||
return __classPrivateFieldGet(this, _AbstractCrdt_instances, "m", _AbstractCrdt_applyCreateList).call(this, op); | ||
} | ||
case OpType.SetParentKey: { | ||
return __classPrivateFieldGet(this, _AbstractCrdt_instances, "m", _AbstractCrdt_applySetParentKey).call(this, op); | ||
} | ||
} | ||
return []; | ||
} | ||
/** | ||
* INTERNAL | ||
*/ | ||
_setParentLink(parent, key) { | ||
if (__classPrivateFieldGet(this, _AbstractCrdt_parent, "f") != null && __classPrivateFieldGet(this, _AbstractCrdt_parent, "f") !== parent) { | ||
throw new Error("Cannot attach parent if it already exist"); | ||
} | ||
__classPrivateFieldSet(this, _AbstractCrdt_parentKey, key, "f"); | ||
__classPrivateFieldSet(this, _AbstractCrdt_parent, parent, "f"); | ||
@@ -272,15 +254,30 @@ } | ||
} | ||
/** | ||
* Subscribes to updates. | ||
*/ | ||
subscribe(listener) { | ||
__classPrivateFieldGet(this, _AbstractCrdt_listeners, "f").push(listener); | ||
} | ||
/** | ||
* Subscribes to updates and children updates. | ||
*/ | ||
subscribeDeep(listener) { | ||
__classPrivateFieldGet(this, _AbstractCrdt_deepListeners, "f").push(listener); | ||
} | ||
/** | ||
* Unsubscribes to updates. | ||
*/ | ||
unsubscribe(listener) { | ||
remove(__classPrivateFieldGet(this, _AbstractCrdt_listeners, "f"), listener); | ||
} | ||
/** | ||
* Unsubscribes to updates and children updates. | ||
*/ | ||
unsubscribeDeep(listener) { | ||
remove(__classPrivateFieldGet(this, _AbstractCrdt_deepListeners, "f"), listener); | ||
} | ||
notify(onlyDeep = false) { | ||
/** | ||
* INTERNAL | ||
*/ | ||
_notify(onlyDeep = false) { | ||
if (onlyDeep === false) { | ||
@@ -295,10 +292,60 @@ for (const listener of __classPrivateFieldGet(this, _AbstractCrdt_listeners, "f")) { | ||
if (this._parent) { | ||
this._parent.notify(true); | ||
this._parent._notify(true); | ||
} | ||
} | ||
} | ||
_AbstractCrdt_listeners = new WeakMap(), _AbstractCrdt_deepListeners = new WeakMap(), _AbstractCrdt_parent = new WeakMap(), _AbstractCrdt_doc = new WeakMap(), _AbstractCrdt_id = new WeakMap(); | ||
_AbstractCrdt_listeners = new WeakMap(), _AbstractCrdt_deepListeners = new WeakMap(), _AbstractCrdt_parent = new WeakMap(), _AbstractCrdt_doc = new WeakMap(), _AbstractCrdt_id = new WeakMap(), _AbstractCrdt_parentKey = new WeakMap(), _AbstractCrdt_instances = new WeakSet(), _AbstractCrdt_applySetParentKey = function _AbstractCrdt_applySetParentKey(op) { | ||
if (this._parent == null) { | ||
return []; | ||
} | ||
if (this._parent instanceof LiveList) { | ||
const previousKey = this._parentKey; | ||
this._parent._setChildKey(op.parentKey, this); | ||
return [ | ||
{ type: OpType.SetParentKey, id: this._id, parentKey: previousKey }, | ||
]; | ||
} | ||
return []; | ||
}, _AbstractCrdt_applyRegister = function _AbstractCrdt_applyRegister(op) { | ||
if (this._doc == null) { | ||
throw new Error("Internal: doc should exist"); | ||
} | ||
if (this._doc.getItem(op.id) != null) { | ||
return []; | ||
} | ||
return this._attachChild(op.id, op.parentKey, new LiveRegister(op.data)); | ||
}, _AbstractCrdt_applyCreateObject = function _AbstractCrdt_applyCreateObject(op) { | ||
if (this._doc == null) { | ||
throw new Error("Internal: doc should exist"); | ||
} | ||
if (this._doc.getItem(op.id) != null) { | ||
return []; | ||
} | ||
return this._attachChild(op.id, op.parentKey, new LiveObject(op.data)); | ||
}, _AbstractCrdt_applyCreateMap = function _AbstractCrdt_applyCreateMap(op) { | ||
if (this._doc == null) { | ||
throw new Error("Internal: doc should exist"); | ||
} | ||
if (this._doc.getItem(op.id) != null) { | ||
return []; | ||
} | ||
return this._attachChild(op.id, op.parentKey, new LiveMap()); | ||
}, _AbstractCrdt_applyCreateList = function _AbstractCrdt_applyCreateList(op) { | ||
if (this._doc == null) { | ||
throw new Error("Internal: doc should exist"); | ||
} | ||
if (this._doc.getItem(op.id) != null) { | ||
return []; | ||
} | ||
return this._attachChild(op.id, op.parentKey, new LiveList()); | ||
}; | ||
/** | ||
* The LiveObject class is similar to a JavaScript object that is synchronized on all clients. | ||
* Keys should be a string, and values should be serializable to JSON. | ||
* If multiple clients update the same property simultaneously, the last modification received by the Liveblocks servers is the winner. | ||
*/ | ||
export class LiveObject extends AbstractCrdt { | ||
constructor(object = {}) { | ||
super(); | ||
_LiveObject_instances.add(this); | ||
_LiveObject_map.set(this, void 0); | ||
@@ -309,3 +356,3 @@ _LiveObject_propToLastUpdate.set(this, new Map()); | ||
if (value instanceof AbstractCrdt) { | ||
value._setParent(this); | ||
value._setParentLink(this, key); | ||
} | ||
@@ -360,3 +407,3 @@ } | ||
const child = deserialize(entry, parentToChildren, doc); | ||
child._setParent(object); | ||
child._setParentLink(object, crdt.parentKey); | ||
__classPrivateFieldGet(object, _LiveObject_map, "f").set(crdt.parentKey, child); | ||
@@ -385,9 +432,26 @@ } | ||
const previousValue = __classPrivateFieldGet(this, _LiveObject_map, "f").get(key); | ||
let result; | ||
if (isCrdt(previousValue)) { | ||
result = previousValue._serialize(this._id, key); | ||
previousValue._detach(); | ||
} | ||
else if (previousValue === undefined) { | ||
result = [ | ||
{ type: OpType.DeleteObjectKey, id: this._id, key: key }, | ||
]; | ||
} | ||
else { | ||
result = [ | ||
{ | ||
type: OpType.UpdateObject, | ||
id: this._id, | ||
data: { [key]: previousValue }, | ||
}, | ||
]; | ||
} | ||
__classPrivateFieldGet(this, _LiveObject_map, "f").set(key, child); | ||
child._setParent(this); | ||
child._setParentLink(this, key); | ||
child._attach(id, this._doc); | ||
this.notify(); | ||
this._notify(); | ||
return result; | ||
} | ||
@@ -406,3 +470,3 @@ /** | ||
} | ||
this.notify(); | ||
this._notify(); | ||
} | ||
@@ -424,7 +488,28 @@ /** | ||
_apply(op) { | ||
var _a; | ||
if (op.type === OpType.UpdateObject) { | ||
const reverse = []; | ||
const reverseUpdate = { | ||
type: OpType.UpdateObject, | ||
id: this._id, | ||
data: {}, | ||
}; | ||
reverse.push(reverseUpdate); | ||
for (const key in op.data) { | ||
const oldValue = __classPrivateFieldGet(this, _LiveObject_map, "f").get(key); | ||
if (oldValue !== undefined) { | ||
reverseUpdate.data[key] = oldValue; | ||
} | ||
else if (oldValue === undefined) { | ||
reverse.push({ type: OpType.DeleteObjectKey, id: this._id, key }); | ||
} | ||
} | ||
for (const key in op.data) { | ||
if (op.opId == null) { | ||
op.opId = (_a = this._doc) === null || _a === void 0 ? void 0 : _a.generateOpId(); | ||
} | ||
const lastOpId = __classPrivateFieldGet(this, _LiveObject_propToLastUpdate, "f").get(key); | ||
if (lastOpId === op.opId) { | ||
__classPrivateFieldGet(this, _LiveObject_propToLastUpdate, "f").delete(key); | ||
continue; | ||
} | ||
@@ -441,17 +526,21 @@ else if (lastOpId != null) { | ||
} | ||
this.notify(); | ||
this._notify(); | ||
return reverse; | ||
} | ||
else if (op.type === OpType.DeleteObjectKey) { | ||
const key = op.key; | ||
const oldValue = __classPrivateFieldGet(this, _LiveObject_map, "f").get(key); | ||
if (isCrdt(oldValue)) { | ||
oldValue._detach(); | ||
} | ||
__classPrivateFieldGet(this, _LiveObject_map, "f").delete(key); | ||
this.notify(); | ||
return __classPrivateFieldGet(this, _LiveObject_instances, "m", _LiveObject_applyDeleteObjectKey).call(this, op); | ||
} | ||
return super._apply(op); | ||
} | ||
/** | ||
* Transform the LiveObject into a javascript object | ||
*/ | ||
toObject() { | ||
return Object.fromEntries(__classPrivateFieldGet(this, _LiveObject_map, "f")); | ||
} | ||
/** | ||
* Adds or updates a property with a specified key and a value. | ||
* @param key The key of the property to add | ||
* @param value The value of the property to add | ||
*/ | ||
set(key, value) { | ||
@@ -461,20 +550,18 @@ // TODO: Find out why typescript complains | ||
} | ||
/** | ||
* Returns a specified property from the LiveObject. | ||
* @param key The key of the property to get | ||
*/ | ||
get(key) { | ||
return __classPrivateFieldGet(this, _LiveObject_map, "f").get(key); | ||
} | ||
/** | ||
* Adds or updates multiple properties at once with an object. | ||
* @param overrides The object used to overrides properties | ||
*/ | ||
update(overrides) { | ||
if (this._doc && this._id) { | ||
const ops = []; | ||
const opId = this._doc.generateOpId(); | ||
const updateOp = { | ||
opId, | ||
id: this._id, | ||
type: OpType.UpdateObject, | ||
data: {}, | ||
}; | ||
ops.push(updateOp); | ||
if (this._doc == null || this._id == null) { | ||
for (const key in overrides) { | ||
__classPrivateFieldGet(this, _LiveObject_propToLastUpdate, "f").set(key, opId); | ||
const oldValue = __classPrivateFieldGet(this, _LiveObject_map, "f").get(key); | ||
if (oldValue instanceof LiveObject) { | ||
if (oldValue instanceof AbstractCrdt) { | ||
oldValue._detach(); | ||
@@ -484,30 +571,80 @@ } | ||
if (newValue instanceof AbstractCrdt) { | ||
newValue._setParent(this); | ||
newValue._attach(this._doc.generateId(), this._doc); | ||
ops.push(...newValue._serialize(this._id, key)); | ||
newValue._setParentLink(this, key); | ||
} | ||
else { | ||
updateOp.data[key] = newValue; | ||
} | ||
__classPrivateFieldGet(this, _LiveObject_map, "f").set(key, newValue); | ||
} | ||
this._doc.dispatch(ops); | ||
this.notify(); | ||
this._notify(); | ||
return; | ||
} | ||
const ops = []; | ||
const reverseOps = []; | ||
const opId = this._doc.generateOpId(); | ||
const updateOp = { | ||
opId, | ||
id: this._id, | ||
type: OpType.UpdateObject, | ||
data: {}, | ||
}; | ||
ops.push(updateOp); | ||
const reverseUpdateOp = { | ||
id: this._id, | ||
type: OpType.UpdateObject, | ||
data: {}, | ||
}; | ||
reverseOps.push(reverseUpdateOp); | ||
for (const key in overrides) { | ||
__classPrivateFieldGet(this, _LiveObject_propToLastUpdate, "f").set(key, opId); | ||
const oldValue = __classPrivateFieldGet(this, _LiveObject_map, "f").get(key); | ||
if (oldValue instanceof AbstractCrdt) { | ||
reverseOps.push(...oldValue._serialize(this._id, key)); | ||
oldValue._detach(); | ||
} | ||
else if (oldValue === undefined) { | ||
reverseOps.push({ type: OpType.DeleteObjectKey, id: this._id, key }); | ||
} | ||
else { | ||
reverseUpdateOp.data[key] = oldValue; | ||
} | ||
const newValue = overrides[key]; | ||
if (newValue instanceof AbstractCrdt) { | ||
newValue._setParent(this); | ||
newValue._setParentLink(this, key); | ||
newValue._attach(this._doc.generateId(), this._doc); | ||
ops.push(...newValue._serialize(this._id, key)); | ||
} | ||
else { | ||
updateOp.data[key] = newValue; | ||
} | ||
__classPrivateFieldGet(this, _LiveObject_map, "f").set(key, newValue); | ||
} | ||
this.notify(); | ||
this._doc.addToUndoStack(reverseOps); | ||
this._doc.dispatch(ops); | ||
this._notify(); | ||
} | ||
} | ||
_LiveObject_map = new WeakMap(), _LiveObject_propToLastUpdate = new WeakMap(); | ||
_LiveObject_map = new WeakMap(), _LiveObject_propToLastUpdate = new WeakMap(), _LiveObject_instances = new WeakSet(), _LiveObject_applyDeleteObjectKey = function _LiveObject_applyDeleteObjectKey(op) { | ||
const key = op.key; | ||
const oldValue = __classPrivateFieldGet(this, _LiveObject_map, "f").get(key); | ||
let result = []; | ||
if (isCrdt(oldValue)) { | ||
result = oldValue._serialize(this._id, op.key); | ||
oldValue._detach(); | ||
} | ||
else if (oldValue !== undefined) { | ||
result = [ | ||
{ | ||
type: OpType.UpdateObject, | ||
id: this._id, | ||
data: { [key]: oldValue }, | ||
}, | ||
]; | ||
} | ||
__classPrivateFieldGet(this, _LiveObject_map, "f").delete(key); | ||
this._notify(); | ||
return result; | ||
}; | ||
/** | ||
* The LiveMap class is similar to a JavaScript Map that is synchronized on all clients. | ||
* Keys should be a string, and values should be serializable to JSON. | ||
* If multiple clients update the same property simultaneously, the last modification received by the Liveblocks servers is the winner. | ||
*/ | ||
export class LiveMap extends AbstractCrdt { | ||
@@ -521,3 +658,3 @@ constructor(entries) { | ||
const value = selfOrRegister(entry[1]); | ||
value._setParent(this); | ||
value._setParentLink(this, entry[0]); | ||
mappedEntries.push([entry[0], value]); | ||
@@ -573,3 +710,3 @@ } | ||
const child = deserialize(entry, parentToChildren, doc); | ||
child._setParent(map); | ||
child._setParentLink(map, crdt.parentKey); | ||
__classPrivateFieldGet(map, _LiveMap_map, "f").set(crdt.parentKey, child); | ||
@@ -598,9 +735,15 @@ } | ||
const previousValue = __classPrivateFieldGet(this, _LiveMap_map, "f").get(key); | ||
let result; | ||
if (previousValue) { | ||
result = previousValue._serialize(this._id, key); | ||
previousValue._detach(); | ||
} | ||
child._setParent(this); | ||
else { | ||
result = [{ type: OpType.DeleteCrdt, id }]; | ||
} | ||
child._setParentLink(this, key); | ||
child._attach(id, this._doc); | ||
__classPrivateFieldGet(this, _LiveMap_map, "f").set(key, child); | ||
this.notify(); | ||
this._notify(); | ||
return result; | ||
} | ||
@@ -626,4 +769,9 @@ /** | ||
child._detach(); | ||
this.notify(); | ||
this._notify(); | ||
} | ||
/** | ||
* Returns a specified element from the LiveMap. | ||
* @param key The key of the element to return. | ||
* @returns The element associated with the specified key, or undefined if the key can't be found in the LiveMap. | ||
*/ | ||
get(key) { | ||
@@ -636,2 +784,7 @@ const value = __classPrivateFieldGet(this, _LiveMap_map, "f").get(key); | ||
} | ||
/** | ||
* Adds or updates an element with a specified key and a value. | ||
* @param key The key of the element to add. Should be a string. | ||
* @param value The value of the element to add. Should be serializable to JSON. | ||
*/ | ||
set(key, value) { | ||
@@ -643,17 +796,32 @@ const oldValue = __classPrivateFieldGet(this, _LiveMap_map, "f").get(key); | ||
const item = selfOrRegister(value); | ||
item._setParent(this); | ||
item._setParentLink(this, key); | ||
__classPrivateFieldGet(this, _LiveMap_map, "f").set(key, item); | ||
if (this._doc && this._id) { | ||
item._attach(this._doc.generateId(), this._doc); | ||
const ops = item._serialize(this._id, key); | ||
this._doc.dispatch(ops); | ||
const id = this._doc.generateId(); | ||
item._attach(id, this._doc); | ||
this._doc.addToUndoStack(oldValue | ||
? oldValue._serialize(this._id, key) | ||
: [{ type: OpType.DeleteCrdt, id }]); | ||
this._doc.dispatch(item._serialize(this._id, key)); | ||
} | ||
this.notify(); | ||
this._notify(); | ||
} | ||
/** | ||
* Returns the number of elements in the LiveMap. | ||
*/ | ||
get size() { | ||
return __classPrivateFieldGet(this, _LiveMap_map, "f").size; | ||
} | ||
/** | ||
* Returns a boolean indicating whether an element with the specified key exists or not. | ||
* @param key The key of the element to test for presence. | ||
*/ | ||
has(key) { | ||
return __classPrivateFieldGet(this, _LiveMap_map, "f").has(key); | ||
} | ||
/** | ||
* Removes the specified element by key. | ||
* @param key The key of the element to remove. | ||
* @returns true if an element existed and has been removed, or false if the element does not exist. | ||
*/ | ||
delete(key) { | ||
@@ -666,8 +834,12 @@ const item = __classPrivateFieldGet(this, _LiveMap_map, "f").get(key); | ||
if (this._doc && item._id) { | ||
this._doc.addToUndoStack(item._serialize(this._id, key)); | ||
this._doc.dispatch([{ type: OpType.DeleteCrdt, id: item._id }]); | ||
} | ||
__classPrivateFieldGet(this, _LiveMap_map, "f").delete(key); | ||
this.notify(); | ||
this._notify(); | ||
return true; | ||
} | ||
/** | ||
* Returns a new Iterator object that contains the [key, value] pairs for each element. | ||
*/ | ||
entries() { | ||
@@ -694,8 +866,17 @@ const innerIterator = __classPrivateFieldGet(this, _LiveMap_map, "f").entries(); | ||
} | ||
/** | ||
* Same function object as the initial value of the entries method. | ||
*/ | ||
[(_LiveMap_map = new WeakMap(), Symbol.iterator)]() { | ||
return this.entries(); | ||
} | ||
/** | ||
* Returns a new Iterator object that contains the keys for each element. | ||
*/ | ||
keys() { | ||
return __classPrivateFieldGet(this, _LiveMap_map, "f").keys(); | ||
} | ||
/** | ||
* Returns a new Iterator object that contains the values for each element. | ||
*/ | ||
values() { | ||
@@ -721,2 +902,6 @@ const innerIterator = __classPrivateFieldGet(this, _LiveMap_map, "f").values(); | ||
} | ||
/** | ||
* Executes a provided function once per each key/value pair in the Map object, in insertion order. | ||
* @param callback Function to execute for each entry in the map. | ||
*/ | ||
forEach(callback) { | ||
@@ -728,2 +913,5 @@ for (const entry of this) { | ||
} | ||
/** | ||
* INTERNAL | ||
*/ | ||
class LiveRegister extends AbstractCrdt { | ||
@@ -772,4 +960,10 @@ constructor(data) { | ||
} | ||
_apply(op) { | ||
return super._apply(op); | ||
} | ||
} | ||
_LiveRegister_data = new WeakMap(); | ||
/** | ||
* The LiveList class represents an ordered collection of items that is synchorinized across clients. | ||
*/ | ||
export class LiveList extends AbstractCrdt { | ||
@@ -800,3 +994,3 @@ constructor(items = []) { | ||
const child = deserialize(entry, parentToChildren, doc); | ||
child._setParent(list); | ||
child._setParentLink(list, entry[1].parentKey); | ||
__classPrivateFieldGet(list, _LiveList_items, "f").push([child, entry[1].parentKey]); | ||
@@ -857,3 +1051,3 @@ __classPrivateFieldGet(list, _LiveList_items, "f").sort((itemA, itemB) => compare(itemA[1], itemB[1])); | ||
child._attach(id, this._doc); | ||
child._setParent(this); | ||
child._setParentLink(this, key); | ||
const index = __classPrivateFieldGet(this, _LiveList_items, "f").findIndex((entry) => entry[1] === key); | ||
@@ -866,3 +1060,4 @@ // Assign a temporary position until we get the fix from the backend | ||
__classPrivateFieldGet(this, _LiveList_items, "f").sort((itemA, itemB) => compare(itemA[1], itemB[1])); | ||
this.notify(); | ||
this._notify(); | ||
return [{ type: OpType.DeleteCrdt, id }]; | ||
} | ||
@@ -878,3 +1073,3 @@ /** | ||
} | ||
this.notify(); | ||
this._notify(); | ||
} | ||
@@ -886,2 +1081,3 @@ /** | ||
var _a; | ||
child._setParentLink(this, key); | ||
const index = __classPrivateFieldGet(this, _LiveList_items, "f").findIndex((entry) => entry[1] === key); | ||
@@ -897,21 +1093,29 @@ // Assign a temporary position until we get the fix from the backend | ||
__classPrivateFieldGet(this, _LiveList_items, "f").sort((itemA, itemB) => compare(itemA[1], itemB[1])); | ||
this.notify(); | ||
this._notify(); | ||
} | ||
/** | ||
* INTERNAL | ||
*/ | ||
_apply(op) { | ||
return super._apply(op); | ||
} | ||
/** | ||
* Returns the number of elements. | ||
*/ | ||
get length() { | ||
return __classPrivateFieldGet(this, _LiveList_items, "f").length; | ||
} | ||
push(item) { | ||
const position = __classPrivateFieldGet(this, _LiveList_items, "f").length === 0 | ||
? makePosition() | ||
: makePosition(__classPrivateFieldGet(this, _LiveList_items, "f")[__classPrivateFieldGet(this, _LiveList_items, "f").length - 1][1]); | ||
const value = selfOrRegister(item); | ||
value._setParent(this); | ||
__classPrivateFieldGet(this, _LiveList_items, "f").push([value, position]); | ||
this.notify(); | ||
if (this._doc && this._id) { | ||
value._attach(this._doc.generateId(), this._doc); | ||
this._doc.dispatch(value._serialize(this._id, position)); | ||
} | ||
/** | ||
* Adds one element to the end of the LiveList. | ||
* @param element The element to add to the end of the LiveList. | ||
*/ | ||
push(element) { | ||
return this.insert(element, this.length); | ||
} | ||
insert(item, index) { | ||
/** | ||
* Inserts one element at a specified index. | ||
* @param element The element to insert. | ||
* @param index The index at which you want to insert the element. | ||
*/ | ||
insert(element, index) { | ||
if (index < 0 || index > __classPrivateFieldGet(this, _LiveList_items, "f").length) { | ||
@@ -923,12 +1127,19 @@ throw new Error(`Cannot delete list item at index "${index}". index should be between 0 and ${__classPrivateFieldGet(this, _LiveList_items, "f").length}`); | ||
const position = makePosition(before, after); | ||
const value = selfOrRegister(item); | ||
value._setParent(this); | ||
const value = selfOrRegister(element); | ||
value._setParentLink(this, position); | ||
__classPrivateFieldGet(this, _LiveList_items, "f").push([value, position]); | ||
__classPrivateFieldGet(this, _LiveList_items, "f").sort((itemA, itemB) => compare(itemA[1], itemB[1])); | ||
this.notify(); | ||
this._notify(); | ||
if (this._doc && this._id) { | ||
value._attach(this._doc.generateId(), this._doc); | ||
const id = this._doc.generateId(); | ||
value._attach(id, this._doc); | ||
this._doc.addToUndoStack([{ type: OpType.DeleteCrdt, id }]); | ||
this._doc.dispatch(value._serialize(this._id, position)); | ||
} | ||
} | ||
/** | ||
* Move one element from one index to another. | ||
* @param index The index of the element to move | ||
* @param targetIndex The index where the element should be after moving. | ||
*/ | ||
move(index, targetIndex) { | ||
@@ -963,6 +1174,15 @@ if (targetIndex < 0) { | ||
const item = __classPrivateFieldGet(this, _LiveList_items, "f")[index]; | ||
const previousPosition = item[1]; | ||
item[1] = position; | ||
item[0]._setParentLink(this, position); | ||
__classPrivateFieldGet(this, _LiveList_items, "f").sort((itemA, itemB) => compare(itemA[1], itemB[1])); | ||
this.notify(); | ||
this._notify(); | ||
if (this._doc && this._id) { | ||
this._doc.addToUndoStack([ | ||
{ | ||
type: OpType.SetParentKey, | ||
id: item[0]._id, | ||
parentKey: previousPosition, | ||
}, | ||
]); | ||
this._doc.dispatch([ | ||
@@ -977,2 +1197,6 @@ { | ||
} | ||
/** | ||
* Deletes an element at the specified index | ||
* @param index The index of the element to delete | ||
*/ | ||
delete(index) { | ||
@@ -988,2 +1212,3 @@ if (index < 0 || index >= __classPrivateFieldGet(this, _LiveList_items, "f").length) { | ||
if (childRecordId) { | ||
this._doc.addToUndoStack(item[0]._serialize(this._id, item[1])); | ||
this._doc.dispatch([ | ||
@@ -997,22 +1222,54 @@ { | ||
} | ||
this.notify(); | ||
this._notify(); | ||
} | ||
/** | ||
* Returns an Array of all the elements in the LiveList. | ||
*/ | ||
toArray() { | ||
return __classPrivateFieldGet(this, _LiveList_items, "f").map((entry) => selfOrRegisterValue(entry[0])); | ||
} | ||
/** | ||
* Tests whether all elements pass the test implemented by the provided function. | ||
* @param predicate Function to test for each element, taking two arguments (the element and its index). | ||
* @returns true if the predicate function returns a truthy value for every element. Otherwise, false. | ||
*/ | ||
every(predicate) { | ||
return this.toArray().every(predicate); | ||
} | ||
/** | ||
* Creates an array with all elements that pass the test implemented by the provided function. | ||
* @param predicate Function to test each element of the LiveList. Return a value that coerces to true to keep the element, or to false otherwise. | ||
* @returns An array with the elements that pass the test. | ||
*/ | ||
filter(predicate) { | ||
return this.toArray().filter(predicate); | ||
} | ||
/** | ||
* Returns the first element that satisfies the provided testing function. | ||
* @param predicate Function to execute on each value. | ||
* @returns The value of the first element in the LiveList that satisfies the provided testing function. Otherwise, undefined is returned. | ||
*/ | ||
find(predicate) { | ||
return this.toArray().find(predicate); | ||
} | ||
/** | ||
* Returns the index of the first element in the LiveList that satisfies the provided testing function. | ||
* @param predicate Function to execute on each value until the function returns true, indicating that the satisfying element was found. | ||
* @returns The index of the first element in the LiveList that passes the test. Otherwise, -1. | ||
*/ | ||
findIndex(predicate) { | ||
return this.toArray().findIndex(predicate); | ||
} | ||
/** | ||
* Executes a provided function once for each element. | ||
* @param callbackfn Function to execute on each element. | ||
*/ | ||
forEach(callbackfn) { | ||
return this.toArray().forEach(callbackfn); | ||
} | ||
/** | ||
* Get the element at the specified index. | ||
* @param index The index on the element to get. | ||
* @returns The element at the specified index or undefined. | ||
*/ | ||
get(index) { | ||
@@ -1024,11 +1281,33 @@ if (index < 0 || index >= __classPrivateFieldGet(this, _LiveList_items, "f").length) { | ||
} | ||
/** | ||
* Returns the first index at which a given element can be found in the LiveList, or -1 if it is not present. | ||
* @param searchElement Element to locate. | ||
* @param fromIndex The index to start the search at. | ||
* @returns The first index of the element in the LiveList; -1 if not found. | ||
*/ | ||
indexOf(searchElement, fromIndex) { | ||
return this.toArray().indexOf(searchElement, fromIndex); | ||
} | ||
/** | ||
* Returns the last index at which a given element can be found in the LiveList, or -1 if it is not present. The LiveLsit is searched backwards, starting at fromIndex. | ||
* @param searchElement Element to locate. | ||
* @param fromIndex The index at which to start searching backwards. | ||
* @returns | ||
*/ | ||
lastIndexOf(searchElement, fromIndex) { | ||
return this.toArray().lastIndexOf(searchElement, fromIndex); | ||
} | ||
/** | ||
* Creates an array populated with the results of calling a provided function on every element. | ||
* @param callback Function that is called for every element. | ||
* @returns An array with each element being the result of the callback function. | ||
*/ | ||
map(callback) { | ||
return __classPrivateFieldGet(this, _LiveList_items, "f").map((entry, i) => callback(selfOrRegisterValue(entry[0]), i)); | ||
} | ||
/** | ||
* Tests whether at least one element in the LiveList passes the test implemented by the provided function. | ||
* @param predicate Function to test for each element. | ||
* @returns true if the callback function returns a truthy value for at least one element. Otherwise, false. | ||
*/ | ||
some(predicate) { | ||
@@ -1035,0 +1314,0 @@ return this.toArray().some(predicate); |
@@ -117,3 +117,3 @@ import { Presence } from "./types"; | ||
export declare type UpdateObjectOp = { | ||
opId: string; | ||
opId?: string; | ||
id: string; | ||
@@ -120,0 +120,0 @@ type: OpType.UpdateObject; |
@@ -84,2 +84,4 @@ import { Others, Presence, ClientOptions, Room, MyPresenceCallback, OthersEventCallback, AuthEndpoint, EventCallback, User, Connection, ErrorCallback, AuthenticationToken, ConnectionCallback } from "./types"; | ||
broadcastEvent: (event: any) => void; | ||
undo: () => void; | ||
redo: () => void; | ||
getStorage: <TRoot>() => Promise<{ | ||
@@ -86,0 +88,0 @@ root: import("./doc").LiveObject<TRoot>; |
@@ -478,2 +478,8 @@ var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, generator) { | ||
} | ||
function undo() { | ||
storage.undo(); | ||
} | ||
function redo() { | ||
storage.redo(); | ||
} | ||
return { | ||
@@ -497,2 +503,4 @@ // Internal | ||
broadcastEvent, | ||
undo, | ||
redo, | ||
getStorage, | ||
@@ -578,2 +586,4 @@ selectors: { | ||
getStorage: machine.getStorage, | ||
undo: machine.undo, | ||
redo: machine.redo, | ||
}; | ||
@@ -580,0 +590,0 @@ return { |
@@ -19,2 +19,4 @@ import { Doc as Doc } from "./doc"; | ||
onMessage(message: ServerMessage): Promise<void>; | ||
undo(): void; | ||
redo(): void; | ||
} |
@@ -51,5 +51,3 @@ var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, generator) { | ||
case ServerMessageType.UpdateStorage: { | ||
for (const op of message.ops) { | ||
(_b = this._doc) === null || _b === void 0 ? void 0 : _b.apply(op); | ||
} | ||
(_b = this._doc) === null || _b === void 0 ? void 0 : _b.apply(message.ops); | ||
break; | ||
@@ -60,2 +58,10 @@ } | ||
} | ||
undo() { | ||
var _a; | ||
(_a = this._doc) === null || _a === void 0 ? void 0 : _a.undo(); | ||
} | ||
redo() { | ||
var _a; | ||
(_a = this._doc) === null || _a === void 0 ? void 0 : _a.redo(); | ||
} | ||
} |
@@ -269,3 +269,5 @@ import { LiveObject } from "./doc"; | ||
}>; | ||
undo: () => void; | ||
redo: () => void; | ||
}; | ||
export {}; |
{ | ||
"name": "@liveblocks/client", | ||
"version": "0.12.0-beta.13", | ||
"version": "0.12.0-beta.14", | ||
"description": "", | ||
@@ -5,0 +5,0 @@ "main": "./lib/cjs/index.js", |
License Policy Violation
LicenseThis package is not allowed per your license policy. Review the package's license to ensure compliance.
Found 1 instance in 1 package
License Policy Violation
LicenseThis package is not allowed per your license policy. Review the package's license to ensure compliance.
Found 1 instance in 1 package
240971
6629