reptree 0.9.0 → 1.0.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +8 -8
- package/dist/index.cjs +444 -404
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.cts +190 -173
- package/dist/index.d.ts +190 -173
- package/dist/index.js +437 -397
- package/dist/index.js.map +1 -1
- package/package.json +1 -3
package/dist/index.cjs
CHANGED
|
@@ -20,21 +20,21 @@ var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: tru
|
|
|
20
20
|
// src/index.ts
|
|
21
21
|
var index_exports = {};
|
|
22
22
|
__export(index_exports, {
|
|
23
|
+
Node: () => Node,
|
|
24
|
+
NodeState: () => NodeState,
|
|
23
25
|
RepTree: () => RepTree,
|
|
24
26
|
StateVector: () => StateVector,
|
|
25
27
|
TreeState: () => TreeState,
|
|
26
|
-
|
|
27
|
-
VertexState: () => VertexState,
|
|
28
|
-
bindVertex: () => bindVertex,
|
|
28
|
+
bindNode: () => bindNode,
|
|
29
29
|
compareOpId: () => compareOpId,
|
|
30
30
|
createOpId: () => createOpId,
|
|
31
31
|
equalsOpId: () => equalsOpId,
|
|
32
32
|
isAnyPropertyOp: () => isAnyPropertyOp,
|
|
33
|
-
|
|
33
|
+
isMoveNodeOp: () => isMoveNodeOp,
|
|
34
34
|
isOpIdGreaterThan: () => isOpIdGreaterThan,
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
35
|
+
newMoveNodeOp: () => newMoveNodeOp,
|
|
36
|
+
newSetNodePropertyOp: () => newSetNodePropertyOp,
|
|
37
|
+
newSetTransientNodePropertyOp: () => newSetTransientNodePropertyOp,
|
|
38
38
|
opIdToString: () => opIdToString,
|
|
39
39
|
tryParseOpIdStr: () => tryParseOpIdStr,
|
|
40
40
|
uuid: () => uuid
|
|
@@ -89,24 +89,24 @@ function opIdToString(opId) {
|
|
|
89
89
|
}
|
|
90
90
|
|
|
91
91
|
// src/operations.ts
|
|
92
|
-
function
|
|
92
|
+
function isMoveNodeOp(op) {
|
|
93
93
|
return "parentId" in op;
|
|
94
94
|
}
|
|
95
95
|
function isAnyPropertyOp(op) {
|
|
96
96
|
return "key" in op;
|
|
97
97
|
}
|
|
98
|
-
function
|
|
98
|
+
function newMoveNodeOp(clock, peerId, targetId, parentId) {
|
|
99
99
|
return { id: createOpId(clock, peerId), targetId, parentId };
|
|
100
100
|
}
|
|
101
|
-
function
|
|
101
|
+
function newSetNodePropertyOp(clock, peerId, targetId, key, value) {
|
|
102
102
|
return { id: createOpId(clock, peerId), targetId, key, value, transient: false };
|
|
103
103
|
}
|
|
104
|
-
function
|
|
104
|
+
function newSetTransientNodePropertyOp(clock, peerId, targetId, key, value) {
|
|
105
105
|
return { id: createOpId(clock, peerId), targetId, key, value, transient: true };
|
|
106
106
|
}
|
|
107
107
|
|
|
108
|
-
// src/
|
|
109
|
-
var
|
|
108
|
+
// src/NodeState.ts
|
|
109
|
+
var NodeState = class {
|
|
110
110
|
constructor(id, parentId) {
|
|
111
111
|
this.id = id;
|
|
112
112
|
this.parentId = parentId;
|
|
@@ -183,21 +183,31 @@ var VertexState = class {
|
|
|
183
183
|
};
|
|
184
184
|
|
|
185
185
|
// src/TreeState.ts
|
|
186
|
-
var
|
|
186
|
+
var _TreeState = class _TreeState {
|
|
187
187
|
constructor() {
|
|
188
188
|
this.changeCallbacks = /* @__PURE__ */ new Map();
|
|
189
189
|
this.globalChangeCallbacks = /* @__PURE__ */ new Set();
|
|
190
190
|
this.batchedEvents = /* @__PURE__ */ new Map();
|
|
191
|
-
this.
|
|
192
|
-
this.batchTickInterval = setInterval(() => {
|
|
193
|
-
this.processBatchedEvents();
|
|
194
|
-
}, 33.3);
|
|
191
|
+
this.nodes = /* @__PURE__ */ new Map();
|
|
195
192
|
}
|
|
196
193
|
dispose() {
|
|
197
|
-
|
|
194
|
+
if (this.batchTickTimeout) {
|
|
195
|
+
clearTimeout(this.batchTickTimeout);
|
|
196
|
+
this.batchTickTimeout = void 0;
|
|
197
|
+
}
|
|
198
|
+
this.batchedEvents.clear();
|
|
199
|
+
}
|
|
200
|
+
scheduleBatchProcessing() {
|
|
201
|
+
if (this.batchTickTimeout) {
|
|
202
|
+
return;
|
|
203
|
+
}
|
|
204
|
+
this.batchTickTimeout = setTimeout(() => {
|
|
205
|
+
this.batchTickTimeout = void 0;
|
|
206
|
+
this.processBatchedEvents();
|
|
207
|
+
}, _TreeState.BATCH_DELAY_MS);
|
|
198
208
|
}
|
|
199
209
|
processBatchedEvents() {
|
|
200
|
-
for (const [
|
|
210
|
+
for (const [nodeId, events] of this.batchedEvents) {
|
|
201
211
|
let lastMoveEvent = null;
|
|
202
212
|
let lastChildrenEvent = null;
|
|
203
213
|
const propertyEventsByKey = /* @__PURE__ */ new Map();
|
|
@@ -218,24 +228,21 @@ var TreeState = class {
|
|
|
218
228
|
...propertyEventsByKey.values()
|
|
219
229
|
];
|
|
220
230
|
this.globalChangeCallbacks.forEach((listener) => listener(filteredEvents));
|
|
221
|
-
this.changeCallbacks.get(
|
|
231
|
+
this.changeCallbacks.get(nodeId)?.forEach((listener) => listener(filteredEvents));
|
|
222
232
|
}
|
|
223
233
|
this.batchedEvents.clear();
|
|
224
234
|
}
|
|
225
|
-
|
|
226
|
-
return Array.from(this.
|
|
235
|
+
getAllNodes() {
|
|
236
|
+
return Array.from(this.nodes.values());
|
|
227
237
|
}
|
|
228
|
-
|
|
229
|
-
return this.
|
|
238
|
+
getNode(id) {
|
|
239
|
+
return this.nodes.get(id);
|
|
230
240
|
}
|
|
231
|
-
getChildrenIds(
|
|
232
|
-
return this.
|
|
241
|
+
getChildrenIds(nodeId) {
|
|
242
|
+
return this.getNode(nodeId)?.children ?? [];
|
|
233
243
|
}
|
|
234
|
-
getChildren(
|
|
235
|
-
return this.getChildrenIds(
|
|
236
|
-
const vertex = this.vertices.get(id);
|
|
237
|
-
return vertex ? vertex : void 0;
|
|
238
|
-
}).filter((vertex) => vertex !== void 0).sort((a, b) => {
|
|
244
|
+
getChildren(nodeId) {
|
|
245
|
+
return this.getChildrenIds(nodeId).map((id) => this.nodes.get(id)).filter((node) => node !== void 0).sort((a, b) => {
|
|
239
246
|
const aDate = a.getProperty("_c");
|
|
240
247
|
const bDate = b.getProperty("_c");
|
|
241
248
|
if (!aDate) return -1;
|
|
@@ -243,100 +250,99 @@ var TreeState = class {
|
|
|
243
250
|
return new Date(aDate).getTime() - new Date(bDate).getTime();
|
|
244
251
|
});
|
|
245
252
|
}
|
|
246
|
-
|
|
247
|
-
let
|
|
248
|
-
const prevParentId =
|
|
249
|
-
if (!
|
|
250
|
-
|
|
251
|
-
this.
|
|
253
|
+
moveNode(nodeId, newParentId) {
|
|
254
|
+
let node = this.getNode(nodeId);
|
|
255
|
+
const prevParentId = node ? node.parentId : void 0;
|
|
256
|
+
if (!node) {
|
|
257
|
+
node = new NodeState(nodeId, newParentId);
|
|
258
|
+
this.nodes.set(nodeId, node);
|
|
252
259
|
}
|
|
253
260
|
if (prevParentId === newParentId) {
|
|
254
|
-
return
|
|
261
|
+
return node;
|
|
255
262
|
}
|
|
256
|
-
|
|
263
|
+
node.parentId = newParentId;
|
|
257
264
|
let childrenInNewParent = null;
|
|
258
265
|
let childrenInOldParent = null;
|
|
259
266
|
if (prevParentId) {
|
|
260
|
-
const
|
|
261
|
-
if (
|
|
262
|
-
|
|
263
|
-
childrenInOldParent =
|
|
267
|
+
const oldParentNode = this.getNode(prevParentId);
|
|
268
|
+
if (oldParentNode) {
|
|
269
|
+
oldParentNode.children = oldParentNode.children.filter((child) => child !== nodeId);
|
|
270
|
+
childrenInOldParent = oldParentNode.children;
|
|
264
271
|
} else {
|
|
265
|
-
console.error(`Old parent
|
|
272
|
+
console.error(`Old parent node not found for ${prevParentId}`);
|
|
266
273
|
}
|
|
267
274
|
}
|
|
268
275
|
if (newParentId !== null) {
|
|
269
|
-
const
|
|
270
|
-
if (
|
|
271
|
-
|
|
272
|
-
childrenInNewParent =
|
|
276
|
+
const newParentNode = this.nodes.get(newParentId);
|
|
277
|
+
if (newParentNode) {
|
|
278
|
+
newParentNode.children.push(nodeId);
|
|
279
|
+
childrenInNewParent = newParentNode.children;
|
|
273
280
|
} else {
|
|
274
|
-
console.error(`New parent
|
|
281
|
+
console.error(`New parent node not found for ${newParentId}`);
|
|
275
282
|
}
|
|
276
283
|
}
|
|
277
284
|
this.notifyChange({
|
|
278
285
|
type: "move",
|
|
279
|
-
|
|
286
|
+
nodeId,
|
|
280
287
|
oldParentId: prevParentId,
|
|
281
288
|
newParentId
|
|
282
289
|
});
|
|
283
290
|
if (childrenInNewParent !== null && newParentId !== null) {
|
|
284
291
|
this.notifyChange({
|
|
285
292
|
type: "children",
|
|
286
|
-
|
|
287
|
-
children: childrenInNewParent.map((id) => this.
|
|
293
|
+
nodeId: newParentId,
|
|
294
|
+
children: childrenInNewParent.map((id) => this.nodes.get(id))
|
|
288
295
|
});
|
|
289
296
|
}
|
|
290
297
|
if (childrenInOldParent !== null && prevParentId) {
|
|
291
298
|
this.notifyChange({
|
|
292
299
|
type: "children",
|
|
293
|
-
|
|
294
|
-
children: childrenInOldParent.map((id) => this.
|
|
300
|
+
nodeId: prevParentId,
|
|
301
|
+
children: childrenInOldParent.map((id) => this.nodes.get(id))
|
|
295
302
|
});
|
|
296
303
|
}
|
|
297
|
-
return
|
|
304
|
+
return node;
|
|
298
305
|
}
|
|
299
|
-
setProperty(
|
|
300
|
-
const
|
|
301
|
-
if (!
|
|
302
|
-
throw new Error(`
|
|
306
|
+
setProperty(nodeId, key, value) {
|
|
307
|
+
const node = this.getNode(nodeId);
|
|
308
|
+
if (!node) {
|
|
309
|
+
throw new Error(`Node ${nodeId} not found`);
|
|
303
310
|
}
|
|
304
|
-
|
|
311
|
+
node.setProperty(key, value);
|
|
305
312
|
this.notifyChange({
|
|
306
313
|
type: "property",
|
|
307
|
-
|
|
314
|
+
nodeId,
|
|
308
315
|
key,
|
|
309
316
|
value
|
|
310
317
|
});
|
|
311
|
-
if (
|
|
318
|
+
if (node.parentId !== null) {
|
|
312
319
|
this.notifyChange({
|
|
313
320
|
type: "children",
|
|
314
|
-
|
|
315
|
-
children:
|
|
316
|
-
// @TODO: shoulld I set all children or rename this property?
|
|
321
|
+
nodeId: node.parentId,
|
|
322
|
+
children: this.getChildren(node.parentId)
|
|
317
323
|
});
|
|
318
324
|
}
|
|
319
325
|
}
|
|
320
|
-
setTransientProperty(
|
|
321
|
-
const
|
|
322
|
-
if (
|
|
323
|
-
|
|
326
|
+
setTransientProperty(nodeId, key, value) {
|
|
327
|
+
const node = this.getNode(nodeId);
|
|
328
|
+
if (node) {
|
|
329
|
+
node.setTransientProperty(key, value);
|
|
324
330
|
}
|
|
325
331
|
this.notifyChange({
|
|
326
332
|
type: "property",
|
|
327
|
-
|
|
333
|
+
nodeId,
|
|
328
334
|
key,
|
|
329
335
|
value
|
|
330
336
|
});
|
|
331
337
|
}
|
|
332
|
-
addChangeCallback(
|
|
333
|
-
if (!this.changeCallbacks.has(
|
|
334
|
-
this.changeCallbacks.set(
|
|
338
|
+
addChangeCallback(nodeId, listener) {
|
|
339
|
+
if (!this.changeCallbacks.has(nodeId)) {
|
|
340
|
+
this.changeCallbacks.set(nodeId, /* @__PURE__ */ new Set());
|
|
335
341
|
}
|
|
336
|
-
this.changeCallbacks.get(
|
|
342
|
+
this.changeCallbacks.get(nodeId).add(listener);
|
|
337
343
|
}
|
|
338
|
-
removeChangeCallback(
|
|
339
|
-
this.changeCallbacks.get(
|
|
344
|
+
removeChangeCallback(nodeId, listener) {
|
|
345
|
+
this.changeCallbacks.get(nodeId)?.delete(listener);
|
|
340
346
|
}
|
|
341
347
|
addGlobalChangeCallback(listener) {
|
|
342
348
|
this.globalChangeCallbacks.add(listener);
|
|
@@ -345,23 +351,24 @@ var TreeState = class {
|
|
|
345
351
|
this.globalChangeCallbacks.delete(listener);
|
|
346
352
|
}
|
|
347
353
|
notifyChange(event) {
|
|
348
|
-
let events = this.batchedEvents.get(event.
|
|
354
|
+
let events = this.batchedEvents.get(event.nodeId);
|
|
349
355
|
if (!events) {
|
|
350
356
|
events = [];
|
|
351
|
-
this.batchedEvents.set(event.
|
|
357
|
+
this.batchedEvents.set(event.nodeId, events);
|
|
352
358
|
}
|
|
353
359
|
events.push(event);
|
|
360
|
+
this.scheduleBatchProcessing();
|
|
354
361
|
}
|
|
355
|
-
printTree(
|
|
362
|
+
printTree(nodeId, indent = "", isLast = true) {
|
|
356
363
|
const prefix = indent + (isLast ? "\u2514\u2500\u2500 " : "\u251C\u2500\u2500 ");
|
|
357
|
-
let result = prefix +
|
|
358
|
-
let
|
|
359
|
-
if (
|
|
360
|
-
const
|
|
361
|
-
if (
|
|
362
|
-
for (const prop of
|
|
364
|
+
let result = prefix + nodeId + "\n";
|
|
365
|
+
let nodeName = null;
|
|
366
|
+
if (nodeId !== null) {
|
|
367
|
+
const node = this.getNode(nodeId);
|
|
368
|
+
if (node) {
|
|
369
|
+
for (const prop of node.getAllProperties()) {
|
|
363
370
|
if (prop.key === "name") {
|
|
364
|
-
|
|
371
|
+
nodeName = prop.value;
|
|
365
372
|
}
|
|
366
373
|
const propPrefix = indent + (isLast ? " " : "\u2502 ") + "\u2022 ";
|
|
367
374
|
result += `${propPrefix}${prop.key}: ${JSON.stringify(prop.value)}
|
|
@@ -369,12 +376,12 @@ var TreeState = class {
|
|
|
369
376
|
}
|
|
370
377
|
}
|
|
371
378
|
}
|
|
372
|
-
const children = this.getChildrenIds(
|
|
379
|
+
const children = this.getChildrenIds(nodeId);
|
|
373
380
|
const sortedChildren = [...children].sort((a, b) => {
|
|
374
|
-
const
|
|
375
|
-
const
|
|
376
|
-
const nameA =
|
|
377
|
-
const nameB =
|
|
381
|
+
const nodeA = this.getNode(a);
|
|
382
|
+
const nodeB = this.getNode(b);
|
|
383
|
+
const nameA = nodeA?.getProperty("name");
|
|
384
|
+
const nameB = nodeB?.getProperty("name");
|
|
378
385
|
if (nameA && nameB) {
|
|
379
386
|
return nameA.localeCompare(nameB);
|
|
380
387
|
}
|
|
@@ -390,6 +397,8 @@ var TreeState = class {
|
|
|
390
397
|
return result;
|
|
391
398
|
}
|
|
392
399
|
};
|
|
400
|
+
_TreeState.BATCH_DELAY_MS = 33.3;
|
|
401
|
+
var TreeState = _TreeState;
|
|
393
402
|
|
|
394
403
|
// src/utils/uuid.ts
|
|
395
404
|
var removeDashes = (guid) => guid.replace(/-/g, "");
|
|
@@ -398,28 +407,28 @@ function uuid() {
|
|
|
398
407
|
}
|
|
399
408
|
|
|
400
409
|
// src/reactive.ts
|
|
401
|
-
function
|
|
410
|
+
function bindNode(tree, id, schemaOrOptions) {
|
|
402
411
|
const isOptions = typeof schemaOrOptions === "object" && schemaOrOptions !== null && (Object.prototype.hasOwnProperty.call(schemaOrOptions, "includeInternalKeys") || Object.prototype.hasOwnProperty.call(schemaOrOptions, "schema"));
|
|
403
412
|
const options = isOptions ? schemaOrOptions : { schema: schemaOrOptions };
|
|
404
413
|
const schema = options.schema;
|
|
405
414
|
const obj = {};
|
|
406
415
|
Object.defineProperties(obj, {
|
|
407
|
-
$
|
|
416
|
+
$node: { get: () => tree.getNode(id), enumerable: false, configurable: true },
|
|
408
417
|
$id: { get: () => id, enumerable: false, configurable: true },
|
|
409
|
-
$parentId: { get: () => tree.
|
|
410
|
-
$parent: { get: () => tree.
|
|
418
|
+
$parentId: { get: () => tree.getNode(id)?.parentId ?? null, enumerable: false, configurable: true },
|
|
419
|
+
$parent: { get: () => tree.getNode(id)?.parent, enumerable: false, configurable: true },
|
|
411
420
|
$children: { get: () => tree.getChildren(id), enumerable: false, configurable: true },
|
|
412
421
|
$childrenIds: { get: () => tree.getChildrenIds(id), enumerable: false, configurable: true },
|
|
413
422
|
$moveTo: {
|
|
414
423
|
value: (parent) => {
|
|
415
424
|
const parentId = typeof parent === "object" && parent !== null ? parent.id || parent.$id : parent;
|
|
416
|
-
tree.
|
|
425
|
+
tree.moveNode(id, parentId);
|
|
417
426
|
},
|
|
418
427
|
enumerable: false,
|
|
419
428
|
configurable: true,
|
|
420
429
|
writable: false
|
|
421
430
|
},
|
|
422
|
-
$delete: { value: () => tree.
|
|
431
|
+
$delete: { value: () => tree.deleteNode(id), enumerable: false, configurable: true, writable: false },
|
|
423
432
|
$observe: { value: (listener) => tree.observe(id, listener), enumerable: false, configurable: true, writable: false },
|
|
424
433
|
$observeChildren: {
|
|
425
434
|
value: (listener) => tree.observe(id, (events) => {
|
|
@@ -431,20 +440,20 @@ function bindVertex(tree, id, schemaOrOptions) {
|
|
|
431
440
|
configurable: true,
|
|
432
441
|
writable: false
|
|
433
442
|
},
|
|
434
|
-
$newChild: { value: (props) => tree.
|
|
435
|
-
$newNamedChild: { value: (name, props) => tree.
|
|
443
|
+
$newChild: { value: (props) => tree.getNode(id).newChild(props), enumerable: false, configurable: true, writable: false },
|
|
444
|
+
$newNamedChild: { value: (name, props) => tree.getNode(id).newNamedChild(name, props), enumerable: false, configurable: true, writable: false },
|
|
436
445
|
$useTransients: {
|
|
437
446
|
value: function(fn) {
|
|
438
447
|
const transientProxy = new Proxy({}, {
|
|
439
448
|
set(_, prop, value) {
|
|
440
449
|
if (typeof prop === "string") {
|
|
441
|
-
tree.
|
|
450
|
+
tree.setTransientNodeProperty(id, prop, value);
|
|
442
451
|
}
|
|
443
452
|
return true;
|
|
444
453
|
},
|
|
445
454
|
get(_, prop) {
|
|
446
455
|
if (typeof prop !== "string") return void 0;
|
|
447
|
-
const rawValue = tree.
|
|
456
|
+
const rawValue = tree.getNodeProperty(id, prop, true);
|
|
448
457
|
return rawValue;
|
|
449
458
|
}
|
|
450
459
|
});
|
|
@@ -475,7 +484,7 @@ function bindVertex(tree, id, schemaOrOptions) {
|
|
|
475
484
|
if (prop in target) {
|
|
476
485
|
return Reflect.get(target, prop, receiver);
|
|
477
486
|
}
|
|
478
|
-
const rawValue = tree.
|
|
487
|
+
const rawValue = tree.getNodeProperty(id, prop, true);
|
|
479
488
|
return rawValue;
|
|
480
489
|
},
|
|
481
490
|
set(target, prop, value) {
|
|
@@ -490,46 +499,46 @@ function bindVertex(tree, id, schemaOrOptions) {
|
|
|
490
499
|
value = res.data;
|
|
491
500
|
}
|
|
492
501
|
}
|
|
493
|
-
tree.
|
|
502
|
+
tree.setNodeProperty(id, prop, value);
|
|
494
503
|
return true;
|
|
495
504
|
},
|
|
496
505
|
deleteProperty(_target, prop) {
|
|
497
506
|
if (typeof prop !== "string") {
|
|
498
507
|
return true;
|
|
499
508
|
}
|
|
500
|
-
tree.
|
|
509
|
+
tree.setNodeProperty(id, prop, void 0);
|
|
501
510
|
return true;
|
|
502
511
|
}
|
|
503
512
|
});
|
|
504
513
|
return proxy;
|
|
505
514
|
}
|
|
506
515
|
|
|
507
|
-
// src/
|
|
508
|
-
var
|
|
516
|
+
// src/Node.ts
|
|
517
|
+
var Node = class {
|
|
509
518
|
constructor(tree, state) {
|
|
510
519
|
this.state = state;
|
|
511
520
|
this._tree = tree;
|
|
512
521
|
}
|
|
513
|
-
/** Returns the tree this
|
|
522
|
+
/** Returns the tree this node belongs to. */
|
|
514
523
|
get tree() {
|
|
515
524
|
return this._tree;
|
|
516
525
|
}
|
|
517
526
|
set tree(value) {
|
|
518
527
|
this._tree = value;
|
|
519
528
|
}
|
|
520
|
-
/** Returns the ID of this
|
|
529
|
+
/** Returns the ID of this node. */
|
|
521
530
|
get id() {
|
|
522
531
|
return this.state.id;
|
|
523
532
|
}
|
|
524
|
-
/** Returns the name of this
|
|
533
|
+
/** Returns the name of this node. The name is stored as a property with the key 'name'. */
|
|
525
534
|
get name() {
|
|
526
535
|
return this.getProperty("name");
|
|
527
536
|
}
|
|
528
|
-
/** Sets the name of this
|
|
537
|
+
/** Sets the name of this node. The name is stored as a property with the key 'name'. */
|
|
529
538
|
set name(name) {
|
|
530
|
-
this.tree.
|
|
539
|
+
this.tree.setNodeProperty(this.id, "name", name);
|
|
531
540
|
}
|
|
532
|
-
/** Returns the creation date of this
|
|
541
|
+
/** Returns the creation date of this node. The creation date is stored as a property with the key '_c'. */
|
|
533
542
|
get createdAt() {
|
|
534
543
|
const createdAt = this.getProperty("_c");
|
|
535
544
|
if (!createdAt) {
|
|
@@ -537,41 +546,41 @@ var Vertex = class {
|
|
|
537
546
|
}
|
|
538
547
|
return new Date(createdAt);
|
|
539
548
|
}
|
|
540
|
-
/** Returns the ID of the parent
|
|
549
|
+
/** Returns the ID of the parent node of this node. */
|
|
541
550
|
get parentId() {
|
|
542
551
|
return this.state.parentId;
|
|
543
552
|
}
|
|
544
|
-
/** Returns the parent
|
|
553
|
+
/** Returns the parent node of this node. */
|
|
545
554
|
get parent() {
|
|
546
555
|
if (!this.parentId) {
|
|
547
556
|
return void 0;
|
|
548
557
|
}
|
|
549
|
-
return this.tree.
|
|
558
|
+
return this.tree.getNode(this.parentId);
|
|
550
559
|
}
|
|
551
|
-
/** Returns the children
|
|
560
|
+
/** Returns the children nodes of this node. */
|
|
552
561
|
get children() {
|
|
553
562
|
return this.tree.getChildren(this.id);
|
|
554
563
|
}
|
|
555
|
-
/** Returns the IDs of the children
|
|
564
|
+
/** Returns the IDs of the children nodes of this node. */
|
|
556
565
|
get childrenIds() {
|
|
557
566
|
return this.tree.getChildrenIds(this.id);
|
|
558
567
|
}
|
|
559
|
-
/** Returns the ancestors of this
|
|
568
|
+
/** Returns the ancestors of this node. The first element is the root node.
|
|
560
569
|
* E.g root -> grandparent -> parent.
|
|
561
|
-
* Doesn't include this
|
|
570
|
+
* Doesn't include this node in the array.
|
|
562
571
|
*/
|
|
563
572
|
get ancestors() {
|
|
564
573
|
return this.tree.getAncestors(this.id);
|
|
565
574
|
}
|
|
566
|
-
/** Returns the ID of the root
|
|
575
|
+
/** Returns the ID of the root node of the tree this node belongs to. */
|
|
567
576
|
get treeId() {
|
|
568
577
|
return this.root.id;
|
|
569
578
|
}
|
|
570
|
-
/** Returns the root
|
|
579
|
+
/** Returns the root node of the tree this node belongs to. */
|
|
571
580
|
get root() {
|
|
572
581
|
const root = this.tree.root;
|
|
573
582
|
if (!root) {
|
|
574
|
-
throw new Error("Root
|
|
583
|
+
throw new Error("Root node of the tree is not set");
|
|
575
584
|
}
|
|
576
585
|
return root;
|
|
577
586
|
}
|
|
@@ -581,48 +590,48 @@ var Vertex = class {
|
|
|
581
590
|
getChildrenAsTypedArray() {
|
|
582
591
|
return this.children.map((v) => v.getAsTypedObject());
|
|
583
592
|
}
|
|
584
|
-
/** Creates a new child
|
|
593
|
+
/** Creates a new child node of this node. */
|
|
585
594
|
newChild(props) {
|
|
586
|
-
return this.tree.
|
|
595
|
+
return this.tree.newNode(this.id, props);
|
|
587
596
|
}
|
|
588
|
-
/** Creates a new named child
|
|
597
|
+
/** Creates a new named child node of this node. */
|
|
589
598
|
newNamedChild(name, props) {
|
|
590
|
-
return this.tree.
|
|
599
|
+
return this.tree.newNamedNode(this.id, name, props);
|
|
591
600
|
}
|
|
592
|
-
/** Sets a property on this
|
|
601
|
+
/** Sets a property on this node. */
|
|
593
602
|
setProperty(key, value) {
|
|
594
603
|
const existingValue = this.getProperty(key, false);
|
|
595
604
|
if (existingValue === value) {
|
|
596
605
|
return;
|
|
597
606
|
}
|
|
598
|
-
this.tree.
|
|
607
|
+
this.tree.setNodeProperty(this.id, key, value);
|
|
599
608
|
}
|
|
600
|
-
/** Sets a transient property on this
|
|
609
|
+
/** Sets a transient property on this node. Transient properties are not persisted to the tree and are not included in the state vector. */
|
|
601
610
|
setTransientProperty(key, value) {
|
|
602
611
|
const existingValue = this.getProperty(key);
|
|
603
612
|
if (existingValue === value) {
|
|
604
613
|
return;
|
|
605
614
|
}
|
|
606
|
-
this.tree.
|
|
615
|
+
this.tree.setTransientNodeProperty(this.id, key, value);
|
|
607
616
|
}
|
|
608
617
|
/** Promotes all transient (temporary) properties to persistent properties. */
|
|
609
618
|
commitTransients() {
|
|
610
619
|
this.tree.commitTransients(this.id);
|
|
611
620
|
}
|
|
612
|
-
/** Sets multiple properties on this
|
|
621
|
+
/** Sets multiple properties on this node. */
|
|
613
622
|
setProperties(props) {
|
|
614
623
|
for (const [key, value] of Object.entries(props)) {
|
|
615
624
|
this.setProperty(key, value);
|
|
616
625
|
}
|
|
617
626
|
}
|
|
618
|
-
/** Returns the value of a property on this
|
|
627
|
+
/** Returns the value of a property on this node. */
|
|
619
628
|
getProperty(key, includingTransient = true) {
|
|
620
|
-
return this.tree.
|
|
629
|
+
return this.tree.getNodeProperty(this.id, key, includingTransient);
|
|
621
630
|
}
|
|
622
|
-
/** Returns all properties on this
|
|
631
|
+
/** Returns all properties on this node. */
|
|
623
632
|
getProperties() {
|
|
624
633
|
const props = {};
|
|
625
|
-
this.tree.
|
|
634
|
+
this.tree.getNodeProperties(this.id).forEach((p) => {
|
|
626
635
|
props[p.key] = p.value;
|
|
627
636
|
});
|
|
628
637
|
return props;
|
|
@@ -630,21 +639,21 @@ var Vertex = class {
|
|
|
630
639
|
findAllChildrenWithProperty(key, value) {
|
|
631
640
|
return this.children.filter((c) => c.getProperty(key) === value);
|
|
632
641
|
}
|
|
633
|
-
|
|
642
|
+
findFirstChildNodeWithProperty(key, value) {
|
|
634
643
|
return this.children.find((c) => c.getProperty(key) === value);
|
|
635
644
|
}
|
|
636
645
|
findFirstTypedChildWithProperty(key, value) {
|
|
637
|
-
return this.
|
|
646
|
+
return this.findFirstChildNodeWithProperty(key, value)?.getAsTypedObject();
|
|
638
647
|
}
|
|
639
648
|
findAllTypedChildrenWithProperty(key, value) {
|
|
640
649
|
return this.findAllChildrenWithProperty(key, value).map((c) => c.getAsTypedObject());
|
|
641
650
|
}
|
|
642
|
-
/** Observes changes to this
|
|
651
|
+
/** Observes changes to this node. */
|
|
643
652
|
observe(listener) {
|
|
644
653
|
const unobserve = this.tree.observe(this.id, listener);
|
|
645
654
|
return () => unobserve();
|
|
646
655
|
}
|
|
647
|
-
/** Observes changes to the children of this
|
|
656
|
+
/** Observes changes to the children of this node. */
|
|
648
657
|
observeChildren(listener) {
|
|
649
658
|
const unobserve = this.tree.observe(this.id, (events) => {
|
|
650
659
|
if (events.some((e) => e.type === "children")) {
|
|
@@ -659,14 +668,14 @@ var Vertex = class {
|
|
|
659
668
|
});
|
|
660
669
|
}
|
|
661
670
|
delete() {
|
|
662
|
-
this.tree.
|
|
671
|
+
this.tree.deleteNode(this.id);
|
|
663
672
|
}
|
|
664
673
|
moveTo(parent) {
|
|
665
|
-
this.tree.
|
|
674
|
+
this.tree.moveNode(this.id, parent.id);
|
|
666
675
|
}
|
|
667
|
-
/** Returns a live reactive object bound to this
|
|
676
|
+
/** Returns a live reactive object bound to this node. Accepts schema or options. */
|
|
668
677
|
bind(schemaOrOptions) {
|
|
669
|
-
return
|
|
678
|
+
return bindNode(this.tree, this.id, schemaOrOptions);
|
|
670
679
|
}
|
|
671
680
|
};
|
|
672
681
|
|
|
@@ -717,7 +726,7 @@ var StateVector = class _StateVector {
|
|
|
717
726
|
/**
|
|
718
727
|
* Updates the state vector with a newly applied operation.
|
|
719
728
|
* Assumes ranges are sorted and non-overlapping.
|
|
720
|
-
*
|
|
729
|
+
*
|
|
721
730
|
* @param peerId The peer ID of the operation
|
|
722
731
|
* @param counter The counter value of the operation
|
|
723
732
|
*/
|
|
@@ -778,7 +787,7 @@ var StateVector = class _StateVector {
|
|
|
778
787
|
}
|
|
779
788
|
/**
|
|
780
789
|
* Updates the state vector with a newly applied operation.
|
|
781
|
-
*
|
|
790
|
+
*
|
|
782
791
|
* @param op The operation that was just applied
|
|
783
792
|
*/
|
|
784
793
|
updateFromOp(op) {
|
|
@@ -794,7 +803,7 @@ var StateVector = class _StateVector {
|
|
|
794
803
|
/**
|
|
795
804
|
* Calculates which operation ranges we have that the other state vector is missing
|
|
796
805
|
* by comparing state vectors.
|
|
797
|
-
*
|
|
806
|
+
*
|
|
798
807
|
* @param other The other state vector to compare against
|
|
799
808
|
* @returns Array of operation ID ranges that we have but they don't
|
|
800
809
|
*/
|
|
@@ -818,7 +827,7 @@ var StateVector = class _StateVector {
|
|
|
818
827
|
}
|
|
819
828
|
/**
|
|
820
829
|
* Checks if the state vector contains the given operation ID
|
|
821
|
-
*
|
|
830
|
+
*
|
|
822
831
|
* @param opId The operation ID to check
|
|
823
832
|
* @returns true if the operation is in the state vector, false otherwise
|
|
824
833
|
*/
|
|
@@ -914,50 +923,54 @@ function isJsonValue(v) {
|
|
|
914
923
|
var _RepTree = class _RepTree {
|
|
915
924
|
/**
|
|
916
925
|
* @param peerId - The peer ID of the current client. Should be unique across all peers.
|
|
917
|
-
* @param ops - The operations to replicate an existing tree, if not provided - an empty tree will be created without a root
|
|
926
|
+
* @param ops - The operations to replicate an existing tree, if not provided - an empty tree will be created without a root node
|
|
918
927
|
*/
|
|
919
928
|
constructor(peerId, ops) {
|
|
920
|
-
this.
|
|
929
|
+
this.moveClock = 0;
|
|
930
|
+
this.propClock = 0;
|
|
921
931
|
this.moveOps = [];
|
|
922
|
-
this.
|
|
923
|
-
this.propertiesAndTheirOpIds = /* @__PURE__ */ new Map();
|
|
932
|
+
this.propertyOpsByKey = /* @__PURE__ */ new Map();
|
|
924
933
|
this.transientPropertiesAndTheirOpIds = /* @__PURE__ */ new Map();
|
|
925
934
|
this.localOps = [];
|
|
926
935
|
this.pendingMovesWithMissingParent = /* @__PURE__ */ new Map();
|
|
927
|
-
this.
|
|
936
|
+
this.pendingPropertiesWithMissingNode = /* @__PURE__ */ new Map();
|
|
928
937
|
this.knownOps = /* @__PURE__ */ new Set();
|
|
929
938
|
this.parentIdBeforeMove = /* @__PURE__ */ new Map();
|
|
930
939
|
this.opAppliedCallbacks = [];
|
|
931
940
|
this._stateVectorEnabled = true;
|
|
932
941
|
this.peerId = peerId;
|
|
933
942
|
this.state = new TreeState();
|
|
934
|
-
this.
|
|
943
|
+
this.moveStateVector = new StateVector();
|
|
944
|
+
this.propStateVector = new StateVector();
|
|
935
945
|
if (ops && ops.length > 0) {
|
|
936
946
|
this.applyOps(ops);
|
|
937
947
|
const root = this.root;
|
|
938
948
|
if (!root) {
|
|
939
|
-
throw new Error("There has to be a root
|
|
949
|
+
throw new Error("There has to be a root node in the operations");
|
|
940
950
|
}
|
|
941
951
|
} else {
|
|
942
|
-
this.
|
|
952
|
+
this.ensureNullNode();
|
|
943
953
|
}
|
|
944
954
|
}
|
|
955
|
+
dispose() {
|
|
956
|
+
this.state.dispose();
|
|
957
|
+
}
|
|
945
958
|
get root() {
|
|
946
|
-
if (!this.
|
|
947
|
-
const
|
|
948
|
-
for (const
|
|
949
|
-
if (
|
|
950
|
-
this.
|
|
951
|
-
return new
|
|
959
|
+
if (!this.rootNodeId) {
|
|
960
|
+
const nodes = this.state.getAllNodes();
|
|
961
|
+
for (const node of nodes) {
|
|
962
|
+
if (node.parentId === null && node.id !== _RepTree.NULL_NODE_ID) {
|
|
963
|
+
this.rootNodeId = node.id;
|
|
964
|
+
return new Node(this, node);
|
|
952
965
|
}
|
|
953
966
|
}
|
|
954
967
|
return void 0;
|
|
955
968
|
}
|
|
956
|
-
const
|
|
957
|
-
if (!
|
|
958
|
-
throw new Error("Root
|
|
969
|
+
const rootNode = this.state.getNode(this.rootNodeId);
|
|
970
|
+
if (!rootNode) {
|
|
971
|
+
throw new Error("Root node not found");
|
|
959
972
|
}
|
|
960
|
-
return new
|
|
973
|
+
return new Node(this, rootNode);
|
|
961
974
|
}
|
|
962
975
|
replicate(newPeerId) {
|
|
963
976
|
return new _RepTree(newPeerId, this.getAllOps());
|
|
@@ -966,54 +979,54 @@ var _RepTree = class _RepTree {
|
|
|
966
979
|
return this.moveOps;
|
|
967
980
|
}
|
|
968
981
|
getAllOps() {
|
|
969
|
-
return [...this.moveOps, ...this.
|
|
982
|
+
return [...this.moveOps, ...this.getPropertyOps()];
|
|
970
983
|
}
|
|
971
|
-
|
|
972
|
-
const
|
|
973
|
-
return
|
|
984
|
+
getNode(nodeId) {
|
|
985
|
+
const node = this.state.getNode(nodeId);
|
|
986
|
+
return node ? new Node(this, node) : void 0;
|
|
974
987
|
}
|
|
975
|
-
|
|
976
|
-
return this.state.
|
|
988
|
+
getAllNodes() {
|
|
989
|
+
return this.state.getAllNodes().map((v) => new Node(this, v));
|
|
977
990
|
}
|
|
978
|
-
getParent(
|
|
979
|
-
const parentId = this.state.
|
|
980
|
-
const parent = parentId ? this.state.
|
|
981
|
-
return parent ? new
|
|
991
|
+
getParent(nodeId) {
|
|
992
|
+
const parentId = this.state.getNode(nodeId)?.parentId;
|
|
993
|
+
const parent = parentId ? this.state.getNode(parentId) : void 0;
|
|
994
|
+
return parent ? new Node(this, parent) : void 0;
|
|
982
995
|
}
|
|
983
|
-
getChildren(
|
|
984
|
-
return this.state.getChildren(
|
|
996
|
+
getChildren(nodeId) {
|
|
997
|
+
return this.state.getChildren(nodeId).map((v) => new Node(this, v));
|
|
985
998
|
}
|
|
986
|
-
getChildrenIds(
|
|
987
|
-
return this.state.getChildrenIds(
|
|
999
|
+
getChildrenIds(nodeId) {
|
|
1000
|
+
return this.state.getChildrenIds(nodeId);
|
|
988
1001
|
}
|
|
989
|
-
/** Returns the ancestors of the given
|
|
990
|
-
getAncestors(
|
|
1002
|
+
/** Returns the ancestors of the given node. The first element is the root node. */
|
|
1003
|
+
getAncestors(nodeId) {
|
|
991
1004
|
const ancestors = [];
|
|
992
|
-
let
|
|
993
|
-
while (
|
|
994
|
-
const
|
|
995
|
-
if (
|
|
996
|
-
ancestors.push(new
|
|
997
|
-
|
|
1005
|
+
let currentNode = this.state.getNode(nodeId);
|
|
1006
|
+
while (currentNode && currentNode.parentId) {
|
|
1007
|
+
const parentNode = this.state.getNode(currentNode.parentId);
|
|
1008
|
+
if (parentNode) {
|
|
1009
|
+
ancestors.push(new Node(this, parentNode));
|
|
1010
|
+
currentNode = parentNode;
|
|
998
1011
|
} else {
|
|
999
1012
|
break;
|
|
1000
1013
|
}
|
|
1001
1014
|
}
|
|
1002
1015
|
return ancestors;
|
|
1003
1016
|
}
|
|
1004
|
-
|
|
1005
|
-
const
|
|
1006
|
-
if (!
|
|
1017
|
+
getNodeProperty(nodeId, key, includingTransient = true) {
|
|
1018
|
+
const node = this.state.getNode(nodeId);
|
|
1019
|
+
if (!node) {
|
|
1007
1020
|
return void 0;
|
|
1008
1021
|
}
|
|
1009
|
-
return
|
|
1022
|
+
return node.getProperty(key, includingTransient);
|
|
1010
1023
|
}
|
|
1011
|
-
|
|
1012
|
-
const
|
|
1013
|
-
if (!
|
|
1024
|
+
getNodeProperties(nodeId) {
|
|
1025
|
+
const node = this.state.getNode(nodeId);
|
|
1026
|
+
if (!node) {
|
|
1014
1027
|
return [];
|
|
1015
1028
|
}
|
|
1016
|
-
return
|
|
1029
|
+
return node.getAllProperties();
|
|
1017
1030
|
}
|
|
1018
1031
|
/**
|
|
1019
1032
|
* Returns all local operations and clears the local operations list.
|
|
@@ -1025,158 +1038,144 @@ var _RepTree = class _RepTree {
|
|
|
1025
1038
|
return ops;
|
|
1026
1039
|
}
|
|
1027
1040
|
/**
|
|
1028
|
-
* This is the first
|
|
1041
|
+
* This is the first node that will contain all other nodes.
|
|
1029
1042
|
* If you plan to replicate a tree then don't use this method and instead merge
|
|
1030
|
-
* in the ops from another tree (that will also contain the root
|
|
1031
|
-
* @returns The root
|
|
1043
|
+
* in the ops from another tree (that will also contain the root node).
|
|
1044
|
+
* @returns The root node
|
|
1032
1045
|
*/
|
|
1033
1046
|
createRoot() {
|
|
1034
|
-
if (this.
|
|
1035
|
-
throw new Error("Root
|
|
1047
|
+
if (this.rootNodeId) {
|
|
1048
|
+
throw new Error("Root node already exists");
|
|
1036
1049
|
}
|
|
1037
|
-
this.
|
|
1038
|
-
const
|
|
1039
|
-
if (!
|
|
1040
|
-
throw new Error("Root
|
|
1050
|
+
this.rootNodeId = this.newNodeInternalWithUUID(null);
|
|
1051
|
+
const rootNode = this.state.getNode(this.rootNodeId);
|
|
1052
|
+
if (!rootNode) {
|
|
1053
|
+
throw new Error("Root node not found");
|
|
1041
1054
|
}
|
|
1042
|
-
return new
|
|
1055
|
+
return new Node(this, rootNode);
|
|
1043
1056
|
}
|
|
1044
|
-
|
|
1057
|
+
newNode(parentId, props = null) {
|
|
1045
1058
|
const typedProps = props;
|
|
1046
|
-
const
|
|
1059
|
+
const nodeId = this.newNodeInternalWithUUID(parentId);
|
|
1047
1060
|
if (typedProps) {
|
|
1048
|
-
this.
|
|
1061
|
+
this.setNodeProperties(nodeId, typedProps);
|
|
1049
1062
|
}
|
|
1050
|
-
const
|
|
1051
|
-
if (!
|
|
1052
|
-
throw new Error("Failed to create
|
|
1063
|
+
const node = this.state.getNode(nodeId);
|
|
1064
|
+
if (!node) {
|
|
1065
|
+
throw new Error("Failed to create node");
|
|
1053
1066
|
}
|
|
1054
|
-
return new
|
|
1067
|
+
return new Node(this, node);
|
|
1055
1068
|
}
|
|
1056
|
-
|
|
1069
|
+
newNamedNode(parentId, name, props = null) {
|
|
1057
1070
|
const typedProps = props;
|
|
1058
|
-
const
|
|
1071
|
+
const nodeId = this.newNodeInternalWithUUID(parentId);
|
|
1059
1072
|
if (typedProps) {
|
|
1060
|
-
this.
|
|
1073
|
+
this.setNodeProperties(nodeId, typedProps);
|
|
1061
1074
|
}
|
|
1062
|
-
this.
|
|
1063
|
-
const
|
|
1064
|
-
if (!
|
|
1065
|
-
throw new Error("Failed to create named
|
|
1075
|
+
this.setNodeProperty(nodeId, "name", name);
|
|
1076
|
+
const node = this.state.getNode(nodeId);
|
|
1077
|
+
if (!node) {
|
|
1078
|
+
throw new Error("Failed to create named node");
|
|
1066
1079
|
}
|
|
1067
|
-
return new
|
|
1080
|
+
return new Node(this, node);
|
|
1068
1081
|
}
|
|
1069
|
-
|
|
1070
|
-
this.
|
|
1071
|
-
const op =
|
|
1082
|
+
moveNode(nodeId, parentId) {
|
|
1083
|
+
this.moveClock++;
|
|
1084
|
+
const op = newMoveNodeOp(this.moveClock, this.peerId, nodeId, parentId);
|
|
1072
1085
|
this.localOps.push(op);
|
|
1073
1086
|
this.applyMove(op);
|
|
1074
1087
|
}
|
|
1075
|
-
|
|
1076
|
-
this.
|
|
1088
|
+
deleteNode(nodeId) {
|
|
1089
|
+
this.moveNode(nodeId, _RepTree.NULL_NODE_ID);
|
|
1077
1090
|
}
|
|
1078
|
-
|
|
1091
|
+
setTransientNodeProperty(nodeId, key, value) {
|
|
1079
1092
|
if (!isJsonValue(value)) {
|
|
1080
1093
|
throw new Error(`Unsupported transient property value for key "${key}"`);
|
|
1081
1094
|
}
|
|
1082
|
-
this.
|
|
1083
|
-
const op =
|
|
1095
|
+
this.propClock++;
|
|
1096
|
+
const op = newSetTransientNodePropertyOp(this.propClock, this.peerId, nodeId, key, value);
|
|
1084
1097
|
this.localOps.push(op);
|
|
1085
1098
|
this.applyProperty(op);
|
|
1086
1099
|
}
|
|
1087
1100
|
/**
|
|
1088
1101
|
* Promotes all transient (temporary) properties to persistent properties.
|
|
1089
|
-
* @param
|
|
1090
|
-
* @returns
|
|
1102
|
+
* @param nodeId - The ID of the node to commit transients for.
|
|
1103
|
+
* @returns
|
|
1091
1104
|
*/
|
|
1092
|
-
commitTransients(
|
|
1093
|
-
const
|
|
1094
|
-
if (!
|
|
1105
|
+
commitTransients(nodeId) {
|
|
1106
|
+
const node = this.state.getNode(nodeId);
|
|
1107
|
+
if (!node) {
|
|
1095
1108
|
return;
|
|
1096
1109
|
}
|
|
1097
|
-
const transientProps =
|
|
1110
|
+
const transientProps = node.getTransientProperties();
|
|
1098
1111
|
for (const prop of transientProps) {
|
|
1099
|
-
this.
|
|
1112
|
+
this.setNodeProperty(nodeId, prop.key, prop.value);
|
|
1100
1113
|
}
|
|
1101
1114
|
for (const prop of transientProps) {
|
|
1102
|
-
this.transientPropertiesAndTheirOpIds.delete(`${prop.key}@${
|
|
1103
|
-
}
|
|
1104
|
-
|
|
1105
|
-
}
|
|
1106
|
-
|
|
1107
|
-
|
|
1108
|
-
if (v === void 0) return true;
|
|
1109
|
-
if (v === null) return true;
|
|
1110
|
-
const t = typeof v;
|
|
1111
|
-
if (t === "string" || t === "number" || t === "boolean") return true;
|
|
1112
|
-
if (t === "bigint" || t === "function" || t === "symbol") return false;
|
|
1113
|
-
if (Array.isArray(v)) return v.every(isJsonValue2);
|
|
1114
|
-
if (t === "object") {
|
|
1115
|
-
if (v instanceof Date) return false;
|
|
1116
|
-
if (v instanceof Map || v instanceof Set || v instanceof RegExp) return false;
|
|
1117
|
-
if (ArrayBuffer.isView(v)) return false;
|
|
1118
|
-
const proto = Object.getPrototypeOf(v);
|
|
1119
|
-
if (proto !== Object.prototype && proto !== null) return false;
|
|
1120
|
-
for (const val of Object.values(v)) {
|
|
1121
|
-
if (!isJsonValue2(val)) return false;
|
|
1122
|
-
}
|
|
1123
|
-
return true;
|
|
1124
|
-
}
|
|
1125
|
-
return false;
|
|
1126
|
-
};
|
|
1127
|
-
if (!isJsonValue2(value)) {
|
|
1115
|
+
this.transientPropertiesAndTheirOpIds.delete(`${prop.key}@${nodeId}`);
|
|
1116
|
+
}
|
|
1117
|
+
node.clearAllTransientProperties();
|
|
1118
|
+
}
|
|
1119
|
+
setNodeProperty(nodeId, key, value) {
|
|
1120
|
+
if (!isJsonValue(value)) {
|
|
1128
1121
|
throw new Error(`Unsupported property value for key "${key}"`);
|
|
1129
1122
|
}
|
|
1130
|
-
this.
|
|
1131
|
-
const op =
|
|
1123
|
+
this.propClock++;
|
|
1124
|
+
const op = newSetNodePropertyOp(this.propClock, this.peerId, nodeId, key, value);
|
|
1132
1125
|
this.localOps.push(op);
|
|
1133
1126
|
this.applyProperty(op);
|
|
1134
1127
|
}
|
|
1135
|
-
|
|
1128
|
+
setNodeProperties(nodeId, props) {
|
|
1136
1129
|
const typedProps = props;
|
|
1137
1130
|
for (const [key, value] of Object.entries(typedProps)) {
|
|
1138
|
-
this.
|
|
1131
|
+
this.setNodeProperty(nodeId, key, value);
|
|
1139
1132
|
}
|
|
1140
1133
|
}
|
|
1141
|
-
|
|
1134
|
+
getNodeByPath(path) {
|
|
1142
1135
|
path = path.replace(/^\/+/, "");
|
|
1143
1136
|
path = path.replace(/\/+$/, "");
|
|
1144
|
-
const
|
|
1145
|
-
if (!
|
|
1137
|
+
const root = this.root;
|
|
1138
|
+
if (!root) {
|
|
1146
1139
|
return void 0;
|
|
1147
1140
|
}
|
|
1148
|
-
|
|
1149
|
-
|
|
1150
|
-
throw new Error("The root vertex is not found");
|
|
1141
|
+
if (path === "") {
|
|
1142
|
+
return root;
|
|
1151
1143
|
}
|
|
1152
|
-
|
|
1153
|
-
return vertex;
|
|
1144
|
+
return this.getNodeByPathArray(root, path.split("/"));
|
|
1154
1145
|
}
|
|
1155
|
-
|
|
1146
|
+
getNodeByPathArray(node, path) {
|
|
1156
1147
|
if (path.length === 0) {
|
|
1157
|
-
return
|
|
1148
|
+
return node ?? void 0;
|
|
1158
1149
|
}
|
|
1159
1150
|
const targetName = path[0];
|
|
1160
|
-
const children = this.getChildren(
|
|
1151
|
+
const children = this.getChildren(node.id);
|
|
1161
1152
|
for (const child of children) {
|
|
1162
1153
|
if (child.getProperty("name") === targetName) {
|
|
1163
|
-
return this.
|
|
1154
|
+
return this.getNodeByPathArray(child, path.slice(1));
|
|
1164
1155
|
}
|
|
1165
1156
|
}
|
|
1166
1157
|
return void 0;
|
|
1167
1158
|
}
|
|
1168
1159
|
printTree() {
|
|
1169
|
-
if (!this.
|
|
1160
|
+
if (!this.rootNodeId) {
|
|
1170
1161
|
return "";
|
|
1171
1162
|
}
|
|
1172
|
-
return this.state.printTree(this.
|
|
1163
|
+
return this.state.printTree(this.rootNodeId);
|
|
1173
1164
|
}
|
|
1174
1165
|
merge(ops) {
|
|
1175
1166
|
this.applyOps(ops);
|
|
1176
1167
|
}
|
|
1177
1168
|
applyOps(ops) {
|
|
1178
|
-
|
|
1179
|
-
|
|
1169
|
+
const moveOps = ops.filter((op) => isMoveNodeOp(op));
|
|
1170
|
+
const propertyOps = ops.filter((op) => isAnyPropertyOp(op));
|
|
1171
|
+
for (const op of moveOps) {
|
|
1172
|
+
if (this.knownOps.has(this.getOpKey(op))) {
|
|
1173
|
+
continue;
|
|
1174
|
+
}
|
|
1175
|
+
this.applyOperation(op);
|
|
1176
|
+
}
|
|
1177
|
+
for (const op of propertyOps) {
|
|
1178
|
+
if (this.knownOps.has(this.getOpKey(op))) {
|
|
1180
1179
|
continue;
|
|
1181
1180
|
}
|
|
1182
1181
|
this.applyOperation(op);
|
|
@@ -1184,7 +1183,7 @@ var _RepTree = class _RepTree {
|
|
|
1184
1183
|
}
|
|
1185
1184
|
/** Applies operations in an optimized way, sorting move ops by OpId to avoid undo-do-redo cycles */
|
|
1186
1185
|
applyOpsOptimizedForLotsOfMoves(ops) {
|
|
1187
|
-
const newMoveOps = ops.filter((op) =>
|
|
1186
|
+
const newMoveOps = ops.filter((op) => isMoveNodeOp(op) && !this.knownOps.has(this.getOpKey(op)));
|
|
1188
1187
|
if (newMoveOps.length > 0) {
|
|
1189
1188
|
const allMoveOps = [...this.moveOps, ...newMoveOps];
|
|
1190
1189
|
allMoveOps.sort((a, b) => compareOpId(a.id, b.id));
|
|
@@ -1193,7 +1192,7 @@ var _RepTree = class _RepTree {
|
|
|
1193
1192
|
this.applyMove(op);
|
|
1194
1193
|
}
|
|
1195
1194
|
}
|
|
1196
|
-
const propertyOps = ops.filter((op) => isAnyPropertyOp(op) && !this.knownOps.has(
|
|
1195
|
+
const propertyOps = ops.filter((op) => isAnyPropertyOp(op) && !this.knownOps.has(this.getOpKey(op)));
|
|
1197
1196
|
for (let i = 0, len = propertyOps.length; i < len; i++) {
|
|
1198
1197
|
const op = propertyOps[i];
|
|
1199
1198
|
this.applyProperty(op);
|
|
@@ -1203,10 +1202,10 @@ var _RepTree = class _RepTree {
|
|
|
1203
1202
|
if (this.root?.id !== other.root?.id) {
|
|
1204
1203
|
return false;
|
|
1205
1204
|
}
|
|
1206
|
-
if (!this.
|
|
1205
|
+
if (!this.rootNodeId) {
|
|
1207
1206
|
return true;
|
|
1208
1207
|
}
|
|
1209
|
-
return _RepTree.
|
|
1208
|
+
return _RepTree.compareNodes(this.rootNodeId, this, other);
|
|
1210
1209
|
}
|
|
1211
1210
|
compareMoveOps(other) {
|
|
1212
1211
|
const movesA = this.moveOps;
|
|
@@ -1224,65 +1223,65 @@ var _RepTree = class _RepTree {
|
|
|
1224
1223
|
/** Checks if the given `ancestorId` is an ancestor of `childId` in the tree */
|
|
1225
1224
|
isAncestor(childId, ancestorId) {
|
|
1226
1225
|
let targetId = childId;
|
|
1227
|
-
let
|
|
1228
|
-
const
|
|
1229
|
-
while (
|
|
1230
|
-
if (
|
|
1231
|
-
if (!
|
|
1232
|
-
if (
|
|
1226
|
+
let node;
|
|
1227
|
+
const visitedNodes = /* @__PURE__ */ new Set();
|
|
1228
|
+
while (node = this.state.getNode(targetId)) {
|
|
1229
|
+
if (node.parentId === ancestorId) return true;
|
|
1230
|
+
if (!node.parentId) return false;
|
|
1231
|
+
if (visitedNodes.has(targetId)) {
|
|
1233
1232
|
console.error(`isAncestor: cycle detected in the tree structure.`);
|
|
1234
1233
|
return false;
|
|
1235
1234
|
}
|
|
1236
|
-
|
|
1237
|
-
targetId =
|
|
1235
|
+
visitedNodes.add(targetId);
|
|
1236
|
+
targetId = node.parentId;
|
|
1238
1237
|
}
|
|
1239
1238
|
return false;
|
|
1240
1239
|
}
|
|
1241
|
-
|
|
1242
|
-
const
|
|
1243
|
-
if (
|
|
1244
|
-
callback(
|
|
1240
|
+
observeNode(nodeId, callback) {
|
|
1241
|
+
const node = this.getNode(nodeId);
|
|
1242
|
+
if (node) {
|
|
1243
|
+
callback(node);
|
|
1245
1244
|
}
|
|
1246
|
-
const unsubscribe = this.observe(
|
|
1247
|
-
const
|
|
1248
|
-
if (
|
|
1249
|
-
callback(
|
|
1245
|
+
const unsubscribe = this.observe(nodeId, (_) => {
|
|
1246
|
+
const node2 = this.getNode(nodeId);
|
|
1247
|
+
if (node2) {
|
|
1248
|
+
callback(node2);
|
|
1250
1249
|
}
|
|
1251
1250
|
});
|
|
1252
1251
|
return () => {
|
|
1253
1252
|
unsubscribe();
|
|
1254
1253
|
};
|
|
1255
1254
|
}
|
|
1256
|
-
|
|
1255
|
+
observeNodeMove(callback) {
|
|
1257
1256
|
const listener = (events) => {
|
|
1258
1257
|
const moveEvent = events.find((e) => e.type === "move");
|
|
1259
1258
|
if (moveEvent) {
|
|
1260
|
-
const
|
|
1261
|
-
if (
|
|
1262
|
-
callback(
|
|
1259
|
+
const node = this.getNode(moveEvent.nodeId);
|
|
1260
|
+
if (node) {
|
|
1261
|
+
callback(node, moveEvent.oldParentId === void 0);
|
|
1263
1262
|
}
|
|
1264
1263
|
}
|
|
1265
1264
|
};
|
|
1266
1265
|
this.state.addGlobalChangeCallback(listener);
|
|
1267
1266
|
return () => this.state.removeGlobalChangeCallback(listener);
|
|
1268
1267
|
}
|
|
1269
|
-
observe(
|
|
1270
|
-
this.state.addChangeCallback(
|
|
1271
|
-
return () => this.state.removeChangeCallback(
|
|
1268
|
+
observe(nodeId, callback) {
|
|
1269
|
+
this.state.addChangeCallback(nodeId, callback);
|
|
1270
|
+
return () => this.state.removeChangeCallback(nodeId, callback);
|
|
1272
1271
|
}
|
|
1273
1272
|
observeOpApplied(callback) {
|
|
1274
1273
|
this.opAppliedCallbacks.push(callback);
|
|
1275
1274
|
return () => this.opAppliedCallbacks = this.opAppliedCallbacks.filter((l) => l !== callback);
|
|
1276
1275
|
}
|
|
1277
|
-
static
|
|
1278
|
-
const childrenA = treeA.state.getChildrenIds(
|
|
1279
|
-
const childrenB = treeB.state.getChildrenIds(
|
|
1276
|
+
static compareNodes(nodeId, treeA, treeB) {
|
|
1277
|
+
const childrenA = treeA.state.getChildrenIds(nodeId);
|
|
1278
|
+
const childrenB = treeB.state.getChildrenIds(nodeId);
|
|
1280
1279
|
if (childrenA.length !== childrenB.length) {
|
|
1281
1280
|
return false;
|
|
1282
1281
|
}
|
|
1283
|
-
if (
|
|
1284
|
-
const propertiesA = treeA.
|
|
1285
|
-
const propertiesB = treeB.
|
|
1282
|
+
if (nodeId !== null) {
|
|
1283
|
+
const propertiesA = treeA.getNodeProperties(nodeId);
|
|
1284
|
+
const propertiesB = treeB.getNodeProperties(nodeId);
|
|
1286
1285
|
if (propertiesA.length !== propertiesB.length) {
|
|
1287
1286
|
return false;
|
|
1288
1287
|
}
|
|
@@ -1296,39 +1295,44 @@ var _RepTree = class _RepTree {
|
|
|
1296
1295
|
if (!childrenB.includes(childId)) {
|
|
1297
1296
|
return false;
|
|
1298
1297
|
}
|
|
1299
|
-
if (!_RepTree.
|
|
1298
|
+
if (!_RepTree.compareNodes(childId, treeA, treeB)) {
|
|
1300
1299
|
return false;
|
|
1301
1300
|
}
|
|
1302
1301
|
}
|
|
1303
1302
|
return true;
|
|
1304
1303
|
}
|
|
1305
|
-
|
|
1306
|
-
this.
|
|
1307
|
-
const op =
|
|
1304
|
+
newNodeInternal(nodeId, parentId) {
|
|
1305
|
+
this.moveClock++;
|
|
1306
|
+
const op = newMoveNodeOp(this.moveClock, this.peerId, nodeId, parentId);
|
|
1308
1307
|
this.localOps.push(op);
|
|
1309
1308
|
this.applyMove(op);
|
|
1310
|
-
this.
|
|
1311
|
-
return
|
|
1309
|
+
this.setNodeProperty(nodeId, "_c", (/* @__PURE__ */ new Date()).toISOString());
|
|
1310
|
+
return nodeId;
|
|
1312
1311
|
}
|
|
1313
|
-
|
|
1314
|
-
const
|
|
1315
|
-
return this.
|
|
1312
|
+
newNodeInternalWithUUID(parentId) {
|
|
1313
|
+
const nodeId = uuid();
|
|
1314
|
+
return this.newNodeInternal(nodeId, parentId);
|
|
1316
1315
|
}
|
|
1317
|
-
|
|
1318
|
-
const
|
|
1319
|
-
if (this.state.
|
|
1316
|
+
ensureNullNode() {
|
|
1317
|
+
const nodeId = _RepTree.NULL_NODE_ID;
|
|
1318
|
+
if (this.state.getNode(nodeId)) {
|
|
1320
1319
|
return;
|
|
1321
1320
|
}
|
|
1322
|
-
this.
|
|
1321
|
+
this.newNodeInternal(nodeId, null);
|
|
1323
1322
|
}
|
|
1324
1323
|
/** Updates the lamport clock with the counter value of the operation */
|
|
1325
|
-
|
|
1326
|
-
if (operation.id.counter > this.
|
|
1327
|
-
this.
|
|
1324
|
+
updateMoveClock(operation) {
|
|
1325
|
+
if (operation.id.counter > this.moveClock) {
|
|
1326
|
+
this.moveClock = operation.id.counter;
|
|
1327
|
+
}
|
|
1328
|
+
}
|
|
1329
|
+
updatePropClock(operation) {
|
|
1330
|
+
if (operation.id.counter > this.propClock) {
|
|
1331
|
+
this.propClock = operation.id.counter;
|
|
1328
1332
|
}
|
|
1329
1333
|
}
|
|
1330
1334
|
applyPendingMovesForParent(parentId) {
|
|
1331
|
-
if (!this.state.
|
|
1335
|
+
if (!this.state.getNode(parentId)) {
|
|
1332
1336
|
return;
|
|
1333
1337
|
}
|
|
1334
1338
|
const pendingMoves = this.pendingMovesWithMissingParent.get(parentId);
|
|
@@ -1341,14 +1345,15 @@ var _RepTree = class _RepTree {
|
|
|
1341
1345
|
}
|
|
1342
1346
|
}
|
|
1343
1347
|
applyMove(op) {
|
|
1344
|
-
if (op.parentId !== null && !this.state.
|
|
1348
|
+
if (op.parentId !== null && !this.state.getNode(op.parentId)) {
|
|
1345
1349
|
if (!this.pendingMovesWithMissingParent.has(op.parentId)) {
|
|
1346
1350
|
this.pendingMovesWithMissingParent.set(op.parentId, []);
|
|
1347
1351
|
}
|
|
1348
1352
|
this.pendingMovesWithMissingParent.get(op.parentId).push(op);
|
|
1353
|
+
this.markOpSeen(op, true);
|
|
1349
1354
|
return;
|
|
1350
1355
|
}
|
|
1351
|
-
this.
|
|
1356
|
+
this.updateMoveClock(op);
|
|
1352
1357
|
const lastOp = this.moveOps.length > 0 ? this.moveOps[this.moveOps.length - 1] : null;
|
|
1353
1358
|
if (lastOp === null || isOpIdGreaterThan(op.id, lastOp.id)) {
|
|
1354
1359
|
this.moveOps.push(op);
|
|
@@ -1375,130 +1380,138 @@ var _RepTree = class _RepTree {
|
|
|
1375
1380
|
this.applyPendingMovesForParent(op.targetId);
|
|
1376
1381
|
}
|
|
1377
1382
|
setLLWPropertyAndItsOpId(op) {
|
|
1378
|
-
this.
|
|
1383
|
+
this.propertyOpsByKey.set(`${op.key}@${op.targetId}`, op);
|
|
1379
1384
|
this.state.setProperty(op.targetId, op.key, op.value);
|
|
1380
|
-
this.reportOpAsApplied(op);
|
|
1385
|
+
this.reportOpAsApplied(op, false);
|
|
1386
|
+
this.refreshPropStateVector();
|
|
1381
1387
|
}
|
|
1382
1388
|
setTransientPropertyAndItsOpId(op) {
|
|
1383
1389
|
this.transientPropertiesAndTheirOpIds.set(`${op.key}@${op.targetId}`, op.id);
|
|
1384
1390
|
this.state.setTransientProperty(op.targetId, op.key, op.value);
|
|
1385
|
-
this.reportOpAsApplied(op);
|
|
1391
|
+
this.reportOpAsApplied(op, false);
|
|
1386
1392
|
}
|
|
1387
1393
|
applyProperty(op) {
|
|
1388
|
-
const
|
|
1389
|
-
if (!
|
|
1394
|
+
const targetNode = this.state.getNode(op.targetId);
|
|
1395
|
+
if (!targetNode) {
|
|
1390
1396
|
if (op.transient) {
|
|
1391
1397
|
return;
|
|
1392
1398
|
}
|
|
1393
|
-
if (!this.
|
|
1394
|
-
this.
|
|
1399
|
+
if (!this.pendingPropertiesWithMissingNode.has(op.targetId)) {
|
|
1400
|
+
this.pendingPropertiesWithMissingNode.set(op.targetId, []);
|
|
1395
1401
|
}
|
|
1396
|
-
this.
|
|
1402
|
+
this.pendingPropertiesWithMissingNode.get(op.targetId).push(op);
|
|
1403
|
+
this.markOpSeen(op, false);
|
|
1397
1404
|
return;
|
|
1398
1405
|
}
|
|
1399
|
-
this.
|
|
1400
|
-
this.applyLLWProperty(op,
|
|
1406
|
+
this.updatePropClock(op);
|
|
1407
|
+
this.applyLLWProperty(op, targetNode);
|
|
1401
1408
|
}
|
|
1402
|
-
applyLLWProperty(op,
|
|
1409
|
+
applyLLWProperty(op, targetNode) {
|
|
1403
1410
|
const prevTransientOpId = this.transientPropertiesAndTheirOpIds.get(`${op.key}@${op.targetId}`);
|
|
1404
|
-
const prevOpId = this.
|
|
1411
|
+
const prevOpId = this.propertyOpsByKey.get(`${op.key}@${op.targetId}`)?.id;
|
|
1405
1412
|
if (!op.transient) {
|
|
1406
|
-
this.setPropertyOps.push(op);
|
|
1407
1413
|
if (!prevOpId || isOpIdGreaterThan(op.id, prevOpId)) {
|
|
1408
1414
|
this.setLLWPropertyAndItsOpId(op);
|
|
1409
1415
|
} else {
|
|
1410
|
-
this.
|
|
1416
|
+
this.markOpSeen(op, false);
|
|
1411
1417
|
}
|
|
1412
1418
|
if (prevTransientOpId && isOpIdGreaterThan(op.id, prevTransientOpId)) {
|
|
1413
1419
|
this.transientPropertiesAndTheirOpIds.delete(`${op.key}@${op.targetId}`);
|
|
1414
|
-
|
|
1420
|
+
targetNode.removeTransientProperty(op.key);
|
|
1415
1421
|
}
|
|
1416
1422
|
} else {
|
|
1417
1423
|
if (!prevTransientOpId || isOpIdGreaterThan(op.id, prevTransientOpId)) {
|
|
1418
1424
|
this.setTransientPropertyAndItsOpId(op);
|
|
1425
|
+
} else {
|
|
1426
|
+
this.markOpSeen(op, false);
|
|
1419
1427
|
}
|
|
1420
1428
|
}
|
|
1421
1429
|
}
|
|
1422
1430
|
applyOperation(op) {
|
|
1423
|
-
if (
|
|
1431
|
+
if (isMoveNodeOp(op)) {
|
|
1424
1432
|
this.applyMove(op);
|
|
1425
1433
|
} else if (isAnyPropertyOp(op)) {
|
|
1426
1434
|
this.applyProperty(op);
|
|
1427
1435
|
}
|
|
1428
1436
|
}
|
|
1429
|
-
|
|
1430
|
-
this.knownOps.add(
|
|
1431
|
-
if (this._stateVectorEnabled) {
|
|
1432
|
-
|
|
1437
|
+
markOpSeen(op, includeInStateVector) {
|
|
1438
|
+
this.knownOps.add(this.getOpKey(op));
|
|
1439
|
+
if (includeInStateVector && this._stateVectorEnabled) {
|
|
1440
|
+
if (isMoveNodeOp(op)) {
|
|
1441
|
+
this.moveStateVector.updateFromOp(op);
|
|
1442
|
+
} else if (isAnyPropertyOp(op)) {
|
|
1443
|
+
this.propStateVector.updateFromOp(op);
|
|
1444
|
+
}
|
|
1433
1445
|
}
|
|
1446
|
+
}
|
|
1447
|
+
reportOpAsApplied(op, includeInStateVector = true) {
|
|
1448
|
+
this.markOpSeen(op, includeInStateVector);
|
|
1434
1449
|
for (const callback of this.opAppliedCallbacks) {
|
|
1435
1450
|
callback(op);
|
|
1436
1451
|
}
|
|
1437
1452
|
}
|
|
1438
1453
|
tryToMove(op) {
|
|
1439
|
-
let
|
|
1440
|
-
if (
|
|
1441
|
-
this.parentIdBeforeMove.set(op.id,
|
|
1454
|
+
let targetNode = this.state.getNode(op.targetId);
|
|
1455
|
+
if (targetNode) {
|
|
1456
|
+
this.parentIdBeforeMove.set(op.id, targetNode.parentId);
|
|
1442
1457
|
}
|
|
1443
1458
|
if (op.targetId === op.parentId) return;
|
|
1444
1459
|
if (op.parentId && this.isAncestor(op.parentId, op.targetId)) return;
|
|
1445
|
-
this.state.
|
|
1446
|
-
if (!
|
|
1447
|
-
const pendingProperties = this.
|
|
1448
|
-
this.
|
|
1460
|
+
this.state.moveNode(op.targetId, op.parentId);
|
|
1461
|
+
if (!targetNode) {
|
|
1462
|
+
const pendingProperties = this.pendingPropertiesWithMissingNode.get(op.targetId) || [];
|
|
1463
|
+
this.pendingPropertiesWithMissingNode.delete(op.targetId);
|
|
1449
1464
|
for (const prop of pendingProperties) {
|
|
1450
1465
|
this.applyProperty(prop);
|
|
1451
1466
|
}
|
|
1452
1467
|
}
|
|
1453
1468
|
}
|
|
1454
1469
|
undoMove(op) {
|
|
1455
|
-
const
|
|
1456
|
-
if (!
|
|
1457
|
-
console.error(`An attempt to undo move operation ${opIdToString(op.id)} failed because the target
|
|
1470
|
+
const targetNode = this.state.getNode(op.targetId);
|
|
1471
|
+
if (!targetNode) {
|
|
1472
|
+
console.error(`An attempt to undo move operation ${opIdToString(op.id)} failed because the target node ${op.targetId} not found`);
|
|
1458
1473
|
return;
|
|
1459
1474
|
}
|
|
1460
1475
|
const prevParentId = this.parentIdBeforeMove.get(op.id);
|
|
1461
1476
|
if (prevParentId === void 0) {
|
|
1462
1477
|
return;
|
|
1463
1478
|
}
|
|
1464
|
-
this.state.
|
|
1479
|
+
this.state.moveNode(op.targetId, prevParentId);
|
|
1465
1480
|
}
|
|
1466
|
-
// --- Range-Based State Vector Methods ---
|
|
1481
|
+
// --- Range-Based State Vector Methods ---
|
|
1467
1482
|
/**
|
|
1468
|
-
* Returns the current state
|
|
1469
|
-
* Returns
|
|
1483
|
+
* Returns the current state vectors for move and property streams.
|
|
1484
|
+
* Returns readonly references to the internal state vectors.
|
|
1470
1485
|
*/
|
|
1471
|
-
|
|
1486
|
+
getStateVectors() {
|
|
1472
1487
|
if (!this._stateVectorEnabled) {
|
|
1473
1488
|
return null;
|
|
1474
1489
|
}
|
|
1475
|
-
return
|
|
1490
|
+
return {
|
|
1491
|
+
move: this.moveStateVector.getState(),
|
|
1492
|
+
prop: this.propStateVector.getState()
|
|
1493
|
+
};
|
|
1476
1494
|
}
|
|
1477
1495
|
/**
|
|
1478
|
-
* Determines which operations are needed to synchronize
|
|
1496
|
+
* Determines which operations are needed to synchronize
|
|
1479
1497
|
* with the provided state vector.
|
|
1480
|
-
*
|
|
1481
|
-
* @param
|
|
1482
|
-
* @returns Operations that should be sent to the other peer, sorted by OpId.
|
|
1498
|
+
*
|
|
1499
|
+
* @param theirStateVectors The state vectors from another peer
|
|
1500
|
+
* @returns Operations that should be sent to the other peer, sorted by OpId within each stream.
|
|
1483
1501
|
*/
|
|
1484
|
-
getMissingOps(
|
|
1502
|
+
getMissingOps(theirStateVectors) {
|
|
1485
1503
|
if (!this._stateVectorEnabled) {
|
|
1486
|
-
return [...this.moveOps, ...this.
|
|
1487
|
-
}
|
|
1488
|
-
const
|
|
1489
|
-
const
|
|
1490
|
-
const
|
|
1491
|
-
const
|
|
1492
|
-
|
|
1493
|
-
|
|
1494
|
-
|
|
1495
|
-
|
|
1496
|
-
|
|
1497
|
-
}
|
|
1498
|
-
}
|
|
1499
|
-
}
|
|
1500
|
-
missingOps.sort((a, b) => compareOpId(a.id, b.id));
|
|
1501
|
-
return missingOps;
|
|
1504
|
+
return [...this.moveOps, ...this.getPropertyOps()];
|
|
1505
|
+
}
|
|
1506
|
+
const otherMoveStateVector = new StateVector(theirStateVectors.move);
|
|
1507
|
+
const otherPropStateVector = new StateVector(theirStateVectors.prop);
|
|
1508
|
+
const missingMoveRanges = this.moveStateVector.diff(otherMoveStateVector);
|
|
1509
|
+
const missingPropRanges = this.propStateVector.diff(otherPropStateVector);
|
|
1510
|
+
const missingMoveOps = this.filterOpsByRanges(this.moveOps, missingMoveRanges);
|
|
1511
|
+
const missingPropOps = this.filterOpsByRanges(this.getPropertyOps(), missingPropRanges);
|
|
1512
|
+
missingMoveOps.sort((a, b) => compareOpId(a.id, b.id));
|
|
1513
|
+
missingPropOps.sort((a, b) => compareOpId(a.id, b.id));
|
|
1514
|
+
return [...missingMoveOps, ...missingPropOps];
|
|
1502
1515
|
}
|
|
1503
1516
|
/**
|
|
1504
1517
|
* Gets or sets whether state vector tracking is enabled
|
|
@@ -1514,43 +1527,70 @@ var _RepTree = class _RepTree {
|
|
|
1514
1527
|
if (value === this._stateVectorEnabled) return;
|
|
1515
1528
|
if (value) {
|
|
1516
1529
|
this._stateVectorEnabled = true;
|
|
1517
|
-
this.
|
|
1530
|
+
this.moveStateVector = StateVector.fromOperations(this.moveOps);
|
|
1531
|
+
this.propStateVector = StateVector.fromOperations(this.getPropertyOps());
|
|
1518
1532
|
} else {
|
|
1519
1533
|
this._stateVectorEnabled = false;
|
|
1520
|
-
this.
|
|
1534
|
+
this.moveStateVector = new StateVector();
|
|
1535
|
+
this.propStateVector = new StateVector();
|
|
1521
1536
|
}
|
|
1522
1537
|
}
|
|
1523
1538
|
/**
|
|
1524
|
-
* Parses the
|
|
1539
|
+
* Parses the node properties with a provided schema that has a `parse` method (e.g., Zod schema)
|
|
1525
1540
|
*/
|
|
1526
|
-
|
|
1527
|
-
const propsArray = this.
|
|
1541
|
+
parseNode(nodeId, schema) {
|
|
1542
|
+
const propsArray = this.getNodeProperties(nodeId);
|
|
1528
1543
|
const propsObject = {};
|
|
1529
1544
|
for (const { key, value } of propsArray) {
|
|
1530
1545
|
propsObject[key] = value;
|
|
1531
1546
|
}
|
|
1532
1547
|
return schema.parse(propsObject);
|
|
1533
1548
|
}
|
|
1549
|
+
getPropertyOps() {
|
|
1550
|
+
return Array.from(this.propertyOpsByKey.values());
|
|
1551
|
+
}
|
|
1552
|
+
refreshPropStateVector() {
|
|
1553
|
+
if (!this._stateVectorEnabled) {
|
|
1554
|
+
return;
|
|
1555
|
+
}
|
|
1556
|
+
this.propStateVector = StateVector.fromOperations(this.getPropertyOps());
|
|
1557
|
+
}
|
|
1558
|
+
getOpKey(op) {
|
|
1559
|
+
const stream = isMoveNodeOp(op) ? "move" : "prop";
|
|
1560
|
+
return `${stream}:${opIdToString(op.id)}`;
|
|
1561
|
+
}
|
|
1562
|
+
filterOpsByRanges(ops, ranges) {
|
|
1563
|
+
const missingOps = [];
|
|
1564
|
+
for (const op of ops) {
|
|
1565
|
+
for (const range of ranges) {
|
|
1566
|
+
if (op.id.peerId === range.peerId && op.id.counter >= range.start && op.id.counter <= range.end) {
|
|
1567
|
+
missingOps.push(op);
|
|
1568
|
+
break;
|
|
1569
|
+
}
|
|
1570
|
+
}
|
|
1571
|
+
}
|
|
1572
|
+
return missingOps;
|
|
1573
|
+
}
|
|
1534
1574
|
};
|
|
1535
|
-
_RepTree.
|
|
1575
|
+
_RepTree.NULL_NODE_ID = "0";
|
|
1536
1576
|
var RepTree = _RepTree;
|
|
1537
1577
|
// Annotate the CommonJS export names for ESM import in node:
|
|
1538
1578
|
0 && (module.exports = {
|
|
1579
|
+
Node,
|
|
1580
|
+
NodeState,
|
|
1539
1581
|
RepTree,
|
|
1540
1582
|
StateVector,
|
|
1541
1583
|
TreeState,
|
|
1542
|
-
|
|
1543
|
-
VertexState,
|
|
1544
|
-
bindVertex,
|
|
1584
|
+
bindNode,
|
|
1545
1585
|
compareOpId,
|
|
1546
1586
|
createOpId,
|
|
1547
1587
|
equalsOpId,
|
|
1548
1588
|
isAnyPropertyOp,
|
|
1549
|
-
|
|
1589
|
+
isMoveNodeOp,
|
|
1550
1590
|
isOpIdGreaterThan,
|
|
1551
|
-
|
|
1552
|
-
|
|
1553
|
-
|
|
1591
|
+
newMoveNodeOp,
|
|
1592
|
+
newSetNodePropertyOp,
|
|
1593
|
+
newSetTransientNodePropertyOp,
|
|
1554
1594
|
opIdToString,
|
|
1555
1595
|
tryParseOpIdStr,
|
|
1556
1596
|
uuid
|