reptree 0.8.2 → 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 -464
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.cts +190 -179
- package/dist/index.d.ts +190 -179
- package/dist/index.js +437 -457
- 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 _Vertex {
|
|
|
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,56 +590,48 @@ var Vertex = class _Vertex {
|
|
|
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
|
-
|
|
587
|
-
throw new Error("Passing children inside props is not supported at the moment");
|
|
588
|
-
}
|
|
589
|
-
const normalized = _Vertex.normalizePropsForCreation(props);
|
|
590
|
-
return this.tree.newVertex(this.id, normalized);
|
|
595
|
+
return this.tree.newNode(this.id, props);
|
|
591
596
|
}
|
|
592
|
-
/** Creates a new named child
|
|
597
|
+
/** Creates a new named child node of this node. */
|
|
593
598
|
newNamedChild(name, props) {
|
|
594
|
-
|
|
595
|
-
throw new Error("Passing children inside props is not supported at the moment");
|
|
596
|
-
}
|
|
597
|
-
const normalized = _Vertex.normalizePropsForCreation(props);
|
|
598
|
-
return this.tree.newNamedVertex(this.id, name, normalized);
|
|
599
|
+
return this.tree.newNamedNode(this.id, name, props);
|
|
599
600
|
}
|
|
600
|
-
/** Sets a property on this
|
|
601
|
+
/** Sets a property on this node. */
|
|
601
602
|
setProperty(key, value) {
|
|
602
603
|
const existingValue = this.getProperty(key, false);
|
|
603
604
|
if (existingValue === value) {
|
|
604
605
|
return;
|
|
605
606
|
}
|
|
606
|
-
this.tree.
|
|
607
|
+
this.tree.setNodeProperty(this.id, key, value);
|
|
607
608
|
}
|
|
608
|
-
/** 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. */
|
|
609
610
|
setTransientProperty(key, value) {
|
|
610
611
|
const existingValue = this.getProperty(key);
|
|
611
612
|
if (existingValue === value) {
|
|
612
613
|
return;
|
|
613
614
|
}
|
|
614
|
-
this.tree.
|
|
615
|
+
this.tree.setTransientNodeProperty(this.id, key, value);
|
|
615
616
|
}
|
|
616
617
|
/** Promotes all transient (temporary) properties to persistent properties. */
|
|
617
618
|
commitTransients() {
|
|
618
619
|
this.tree.commitTransients(this.id);
|
|
619
620
|
}
|
|
620
|
-
/** Sets multiple properties on this
|
|
621
|
+
/** Sets multiple properties on this node. */
|
|
621
622
|
setProperties(props) {
|
|
622
623
|
for (const [key, value] of Object.entries(props)) {
|
|
623
624
|
this.setProperty(key, value);
|
|
624
625
|
}
|
|
625
626
|
}
|
|
626
|
-
/** Returns the value of a property on this
|
|
627
|
+
/** Returns the value of a property on this node. */
|
|
627
628
|
getProperty(key, includingTransient = true) {
|
|
628
|
-
return this.tree.
|
|
629
|
+
return this.tree.getNodeProperty(this.id, key, includingTransient);
|
|
629
630
|
}
|
|
630
|
-
/** Returns all properties on this
|
|
631
|
+
/** Returns all properties on this node. */
|
|
631
632
|
getProperties() {
|
|
632
633
|
const props = {};
|
|
633
|
-
this.tree.
|
|
634
|
+
this.tree.getNodeProperties(this.id).forEach((p) => {
|
|
634
635
|
props[p.key] = p.value;
|
|
635
636
|
});
|
|
636
637
|
return props;
|
|
@@ -638,21 +639,21 @@ var Vertex = class _Vertex {
|
|
|
638
639
|
findAllChildrenWithProperty(key, value) {
|
|
639
640
|
return this.children.filter((c) => c.getProperty(key) === value);
|
|
640
641
|
}
|
|
641
|
-
|
|
642
|
+
findFirstChildNodeWithProperty(key, value) {
|
|
642
643
|
return this.children.find((c) => c.getProperty(key) === value);
|
|
643
644
|
}
|
|
644
645
|
findFirstTypedChildWithProperty(key, value) {
|
|
645
|
-
return this.
|
|
646
|
+
return this.findFirstChildNodeWithProperty(key, value)?.getAsTypedObject();
|
|
646
647
|
}
|
|
647
648
|
findAllTypedChildrenWithProperty(key, value) {
|
|
648
649
|
return this.findAllChildrenWithProperty(key, value).map((c) => c.getAsTypedObject());
|
|
649
650
|
}
|
|
650
|
-
/** Observes changes to this
|
|
651
|
+
/** Observes changes to this node. */
|
|
651
652
|
observe(listener) {
|
|
652
653
|
const unobserve = this.tree.observe(this.id, listener);
|
|
653
654
|
return () => unobserve();
|
|
654
655
|
}
|
|
655
|
-
/** Observes changes to the children of this
|
|
656
|
+
/** Observes changes to the children of this node. */
|
|
656
657
|
observeChildren(listener) {
|
|
657
658
|
const unobserve = this.tree.observe(this.id, (events) => {
|
|
658
659
|
if (events.some((e) => e.type === "children")) {
|
|
@@ -667,66 +668,14 @@ var Vertex = class _Vertex {
|
|
|
667
668
|
});
|
|
668
669
|
}
|
|
669
670
|
delete() {
|
|
670
|
-
this.tree.
|
|
671
|
+
this.tree.deleteNode(this.id);
|
|
671
672
|
}
|
|
672
673
|
moveTo(parent) {
|
|
673
|
-
this.tree.
|
|
674
|
+
this.tree.moveNode(this.id, parent.id);
|
|
674
675
|
}
|
|
675
|
-
/** Returns a live reactive object bound to this
|
|
676
|
+
/** Returns a live reactive object bound to this node. Accepts schema or options. */
|
|
676
677
|
bind(schemaOrOptions) {
|
|
677
|
-
return
|
|
678
|
-
}
|
|
679
|
-
/**
|
|
680
|
-
* Normalizes an input props object for vertex creation:
|
|
681
|
-
* - Filters unsupported field types with a console warning
|
|
682
|
-
* - When a name param is provided to newNamedChild, ignores conflicting name in props
|
|
683
|
-
*/
|
|
684
|
-
static normalizePropsForCreation(props) {
|
|
685
|
-
if (!props) return null;
|
|
686
|
-
const input = props;
|
|
687
|
-
const out = {};
|
|
688
|
-
const skipped = [];
|
|
689
|
-
const isJsonValue2 = (v) => {
|
|
690
|
-
if (v === null) return true;
|
|
691
|
-
const t = typeof v;
|
|
692
|
-
if (t === "string" || t === "number" || t === "boolean") return true;
|
|
693
|
-
if (Array.isArray(v)) return v.every(isJsonValue2);
|
|
694
|
-
if (t === "object") {
|
|
695
|
-
const proto = Object.getPrototypeOf(v);
|
|
696
|
-
if (proto !== Object.prototype && proto !== null) return false;
|
|
697
|
-
for (const val of Object.values(v)) {
|
|
698
|
-
if (!isJsonValue2(val)) return false;
|
|
699
|
-
}
|
|
700
|
-
return true;
|
|
701
|
-
}
|
|
702
|
-
return false;
|
|
703
|
-
};
|
|
704
|
-
for (const [rawKey, rawValue] of Object.entries(input)) {
|
|
705
|
-
if (rawValue === void 0) {
|
|
706
|
-
continue;
|
|
707
|
-
}
|
|
708
|
-
if (rawKey === "children") continue;
|
|
709
|
-
let key = rawKey;
|
|
710
|
-
let value = rawValue;
|
|
711
|
-
if (key === "_c") {
|
|
712
|
-
if (value instanceof Date) {
|
|
713
|
-
value = value.toISOString();
|
|
714
|
-
} else if (typeof value === "string") {
|
|
715
|
-
} else {
|
|
716
|
-
skipped.push(rawKey);
|
|
717
|
-
continue;
|
|
718
|
-
}
|
|
719
|
-
}
|
|
720
|
-
if (!isJsonValue2(value)) {
|
|
721
|
-
skipped.push(rawKey);
|
|
722
|
-
continue;
|
|
723
|
-
}
|
|
724
|
-
out[key] = value;
|
|
725
|
-
}
|
|
726
|
-
if (skipped.length > 0) {
|
|
727
|
-
throw new Error(`Unsupported property types for keys: ${skipped.join(", ")}`);
|
|
728
|
-
}
|
|
729
|
-
return Object.keys(out).length > 0 ? out : null;
|
|
678
|
+
return bindNode(this.tree, this.id, schemaOrOptions);
|
|
730
679
|
}
|
|
731
680
|
};
|
|
732
681
|
|
|
@@ -777,7 +726,7 @@ var StateVector = class _StateVector {
|
|
|
777
726
|
/**
|
|
778
727
|
* Updates the state vector with a newly applied operation.
|
|
779
728
|
* Assumes ranges are sorted and non-overlapping.
|
|
780
|
-
*
|
|
729
|
+
*
|
|
781
730
|
* @param peerId The peer ID of the operation
|
|
782
731
|
* @param counter The counter value of the operation
|
|
783
732
|
*/
|
|
@@ -838,7 +787,7 @@ var StateVector = class _StateVector {
|
|
|
838
787
|
}
|
|
839
788
|
/**
|
|
840
789
|
* Updates the state vector with a newly applied operation.
|
|
841
|
-
*
|
|
790
|
+
*
|
|
842
791
|
* @param op The operation that was just applied
|
|
843
792
|
*/
|
|
844
793
|
updateFromOp(op) {
|
|
@@ -854,7 +803,7 @@ var StateVector = class _StateVector {
|
|
|
854
803
|
/**
|
|
855
804
|
* Calculates which operation ranges we have that the other state vector is missing
|
|
856
805
|
* by comparing state vectors.
|
|
857
|
-
*
|
|
806
|
+
*
|
|
858
807
|
* @param other The other state vector to compare against
|
|
859
808
|
* @returns Array of operation ID ranges that we have but they don't
|
|
860
809
|
*/
|
|
@@ -878,7 +827,7 @@ var StateVector = class _StateVector {
|
|
|
878
827
|
}
|
|
879
828
|
/**
|
|
880
829
|
* Checks if the state vector contains the given operation ID
|
|
881
|
-
*
|
|
830
|
+
*
|
|
882
831
|
* @param opId The operation ID to check
|
|
883
832
|
* @returns true if the operation is in the state vector, false otherwise
|
|
884
833
|
*/
|
|
@@ -974,50 +923,54 @@ function isJsonValue(v) {
|
|
|
974
923
|
var _RepTree = class _RepTree {
|
|
975
924
|
/**
|
|
976
925
|
* @param peerId - The peer ID of the current client. Should be unique across all peers.
|
|
977
|
-
* @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
|
|
978
927
|
*/
|
|
979
928
|
constructor(peerId, ops) {
|
|
980
|
-
this.
|
|
929
|
+
this.moveClock = 0;
|
|
930
|
+
this.propClock = 0;
|
|
981
931
|
this.moveOps = [];
|
|
982
|
-
this.
|
|
983
|
-
this.propertiesAndTheirOpIds = /* @__PURE__ */ new Map();
|
|
932
|
+
this.propertyOpsByKey = /* @__PURE__ */ new Map();
|
|
984
933
|
this.transientPropertiesAndTheirOpIds = /* @__PURE__ */ new Map();
|
|
985
934
|
this.localOps = [];
|
|
986
935
|
this.pendingMovesWithMissingParent = /* @__PURE__ */ new Map();
|
|
987
|
-
this.
|
|
936
|
+
this.pendingPropertiesWithMissingNode = /* @__PURE__ */ new Map();
|
|
988
937
|
this.knownOps = /* @__PURE__ */ new Set();
|
|
989
938
|
this.parentIdBeforeMove = /* @__PURE__ */ new Map();
|
|
990
939
|
this.opAppliedCallbacks = [];
|
|
991
940
|
this._stateVectorEnabled = true;
|
|
992
941
|
this.peerId = peerId;
|
|
993
942
|
this.state = new TreeState();
|
|
994
|
-
this.
|
|
943
|
+
this.moveStateVector = new StateVector();
|
|
944
|
+
this.propStateVector = new StateVector();
|
|
995
945
|
if (ops && ops.length > 0) {
|
|
996
946
|
this.applyOps(ops);
|
|
997
947
|
const root = this.root;
|
|
998
948
|
if (!root) {
|
|
999
|
-
throw new Error("There has to be a root
|
|
949
|
+
throw new Error("There has to be a root node in the operations");
|
|
1000
950
|
}
|
|
1001
951
|
} else {
|
|
1002
|
-
this.
|
|
952
|
+
this.ensureNullNode();
|
|
1003
953
|
}
|
|
1004
954
|
}
|
|
955
|
+
dispose() {
|
|
956
|
+
this.state.dispose();
|
|
957
|
+
}
|
|
1005
958
|
get root() {
|
|
1006
|
-
if (!this.
|
|
1007
|
-
const
|
|
1008
|
-
for (const
|
|
1009
|
-
if (
|
|
1010
|
-
this.
|
|
1011
|
-
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);
|
|
1012
965
|
}
|
|
1013
966
|
}
|
|
1014
967
|
return void 0;
|
|
1015
968
|
}
|
|
1016
|
-
const
|
|
1017
|
-
if (!
|
|
1018
|
-
throw new Error("Root
|
|
969
|
+
const rootNode = this.state.getNode(this.rootNodeId);
|
|
970
|
+
if (!rootNode) {
|
|
971
|
+
throw new Error("Root node not found");
|
|
1019
972
|
}
|
|
1020
|
-
return new
|
|
973
|
+
return new Node(this, rootNode);
|
|
1021
974
|
}
|
|
1022
975
|
replicate(newPeerId) {
|
|
1023
976
|
return new _RepTree(newPeerId, this.getAllOps());
|
|
@@ -1026,54 +979,54 @@ var _RepTree = class _RepTree {
|
|
|
1026
979
|
return this.moveOps;
|
|
1027
980
|
}
|
|
1028
981
|
getAllOps() {
|
|
1029
|
-
return [...this.moveOps, ...this.
|
|
982
|
+
return [...this.moveOps, ...this.getPropertyOps()];
|
|
1030
983
|
}
|
|
1031
|
-
|
|
1032
|
-
const
|
|
1033
|
-
return
|
|
984
|
+
getNode(nodeId) {
|
|
985
|
+
const node = this.state.getNode(nodeId);
|
|
986
|
+
return node ? new Node(this, node) : void 0;
|
|
1034
987
|
}
|
|
1035
|
-
|
|
1036
|
-
return this.state.
|
|
988
|
+
getAllNodes() {
|
|
989
|
+
return this.state.getAllNodes().map((v) => new Node(this, v));
|
|
1037
990
|
}
|
|
1038
|
-
getParent(
|
|
1039
|
-
const parentId = this.state.
|
|
1040
|
-
const parent = parentId ? this.state.
|
|
1041
|
-
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;
|
|
1042
995
|
}
|
|
1043
|
-
getChildren(
|
|
1044
|
-
return this.state.getChildren(
|
|
996
|
+
getChildren(nodeId) {
|
|
997
|
+
return this.state.getChildren(nodeId).map((v) => new Node(this, v));
|
|
1045
998
|
}
|
|
1046
|
-
getChildrenIds(
|
|
1047
|
-
return this.state.getChildrenIds(
|
|
999
|
+
getChildrenIds(nodeId) {
|
|
1000
|
+
return this.state.getChildrenIds(nodeId);
|
|
1048
1001
|
}
|
|
1049
|
-
/** Returns the ancestors of the given
|
|
1050
|
-
getAncestors(
|
|
1002
|
+
/** Returns the ancestors of the given node. The first element is the root node. */
|
|
1003
|
+
getAncestors(nodeId) {
|
|
1051
1004
|
const ancestors = [];
|
|
1052
|
-
let
|
|
1053
|
-
while (
|
|
1054
|
-
const
|
|
1055
|
-
if (
|
|
1056
|
-
ancestors.push(new
|
|
1057
|
-
|
|
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;
|
|
1058
1011
|
} else {
|
|
1059
1012
|
break;
|
|
1060
1013
|
}
|
|
1061
1014
|
}
|
|
1062
1015
|
return ancestors;
|
|
1063
1016
|
}
|
|
1064
|
-
|
|
1065
|
-
const
|
|
1066
|
-
if (!
|
|
1017
|
+
getNodeProperty(nodeId, key, includingTransient = true) {
|
|
1018
|
+
const node = this.state.getNode(nodeId);
|
|
1019
|
+
if (!node) {
|
|
1067
1020
|
return void 0;
|
|
1068
1021
|
}
|
|
1069
|
-
return
|
|
1022
|
+
return node.getProperty(key, includingTransient);
|
|
1070
1023
|
}
|
|
1071
|
-
|
|
1072
|
-
const
|
|
1073
|
-
if (!
|
|
1024
|
+
getNodeProperties(nodeId) {
|
|
1025
|
+
const node = this.state.getNode(nodeId);
|
|
1026
|
+
if (!node) {
|
|
1074
1027
|
return [];
|
|
1075
1028
|
}
|
|
1076
|
-
return
|
|
1029
|
+
return node.getAllProperties();
|
|
1077
1030
|
}
|
|
1078
1031
|
/**
|
|
1079
1032
|
* Returns all local operations and clears the local operations list.
|
|
@@ -1085,158 +1038,144 @@ var _RepTree = class _RepTree {
|
|
|
1085
1038
|
return ops;
|
|
1086
1039
|
}
|
|
1087
1040
|
/**
|
|
1088
|
-
* This is the first
|
|
1041
|
+
* This is the first node that will contain all other nodes.
|
|
1089
1042
|
* If you plan to replicate a tree then don't use this method and instead merge
|
|
1090
|
-
* in the ops from another tree (that will also contain the root
|
|
1091
|
-
* @returns The root
|
|
1043
|
+
* in the ops from another tree (that will also contain the root node).
|
|
1044
|
+
* @returns The root node
|
|
1092
1045
|
*/
|
|
1093
1046
|
createRoot() {
|
|
1094
|
-
if (this.
|
|
1095
|
-
throw new Error("Root
|
|
1047
|
+
if (this.rootNodeId) {
|
|
1048
|
+
throw new Error("Root node already exists");
|
|
1096
1049
|
}
|
|
1097
|
-
this.
|
|
1098
|
-
const
|
|
1099
|
-
if (!
|
|
1100
|
-
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");
|
|
1101
1054
|
}
|
|
1102
|
-
return new
|
|
1055
|
+
return new Node(this, rootNode);
|
|
1103
1056
|
}
|
|
1104
|
-
|
|
1057
|
+
newNode(parentId, props = null) {
|
|
1105
1058
|
const typedProps = props;
|
|
1106
|
-
const
|
|
1059
|
+
const nodeId = this.newNodeInternalWithUUID(parentId);
|
|
1107
1060
|
if (typedProps) {
|
|
1108
|
-
this.
|
|
1061
|
+
this.setNodeProperties(nodeId, typedProps);
|
|
1109
1062
|
}
|
|
1110
|
-
const
|
|
1111
|
-
if (!
|
|
1112
|
-
throw new Error("Failed to create
|
|
1063
|
+
const node = this.state.getNode(nodeId);
|
|
1064
|
+
if (!node) {
|
|
1065
|
+
throw new Error("Failed to create node");
|
|
1113
1066
|
}
|
|
1114
|
-
return new
|
|
1067
|
+
return new Node(this, node);
|
|
1115
1068
|
}
|
|
1116
|
-
|
|
1069
|
+
newNamedNode(parentId, name, props = null) {
|
|
1117
1070
|
const typedProps = props;
|
|
1118
|
-
const
|
|
1071
|
+
const nodeId = this.newNodeInternalWithUUID(parentId);
|
|
1119
1072
|
if (typedProps) {
|
|
1120
|
-
this.
|
|
1073
|
+
this.setNodeProperties(nodeId, typedProps);
|
|
1121
1074
|
}
|
|
1122
|
-
this.
|
|
1123
|
-
const
|
|
1124
|
-
if (!
|
|
1125
|
-
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");
|
|
1126
1079
|
}
|
|
1127
|
-
return new
|
|
1080
|
+
return new Node(this, node);
|
|
1128
1081
|
}
|
|
1129
|
-
|
|
1130
|
-
this.
|
|
1131
|
-
const op =
|
|
1082
|
+
moveNode(nodeId, parentId) {
|
|
1083
|
+
this.moveClock++;
|
|
1084
|
+
const op = newMoveNodeOp(this.moveClock, this.peerId, nodeId, parentId);
|
|
1132
1085
|
this.localOps.push(op);
|
|
1133
1086
|
this.applyMove(op);
|
|
1134
1087
|
}
|
|
1135
|
-
|
|
1136
|
-
this.
|
|
1088
|
+
deleteNode(nodeId) {
|
|
1089
|
+
this.moveNode(nodeId, _RepTree.NULL_NODE_ID);
|
|
1137
1090
|
}
|
|
1138
|
-
|
|
1091
|
+
setTransientNodeProperty(nodeId, key, value) {
|
|
1139
1092
|
if (!isJsonValue(value)) {
|
|
1140
1093
|
throw new Error(`Unsupported transient property value for key "${key}"`);
|
|
1141
1094
|
}
|
|
1142
|
-
this.
|
|
1143
|
-
const op =
|
|
1095
|
+
this.propClock++;
|
|
1096
|
+
const op = newSetTransientNodePropertyOp(this.propClock, this.peerId, nodeId, key, value);
|
|
1144
1097
|
this.localOps.push(op);
|
|
1145
1098
|
this.applyProperty(op);
|
|
1146
1099
|
}
|
|
1147
1100
|
/**
|
|
1148
1101
|
* Promotes all transient (temporary) properties to persistent properties.
|
|
1149
|
-
* @param
|
|
1150
|
-
* @returns
|
|
1102
|
+
* @param nodeId - The ID of the node to commit transients for.
|
|
1103
|
+
* @returns
|
|
1151
1104
|
*/
|
|
1152
|
-
commitTransients(
|
|
1153
|
-
const
|
|
1154
|
-
if (!
|
|
1105
|
+
commitTransients(nodeId) {
|
|
1106
|
+
const node = this.state.getNode(nodeId);
|
|
1107
|
+
if (!node) {
|
|
1155
1108
|
return;
|
|
1156
1109
|
}
|
|
1157
|
-
const transientProps =
|
|
1110
|
+
const transientProps = node.getTransientProperties();
|
|
1158
1111
|
for (const prop of transientProps) {
|
|
1159
|
-
this.
|
|
1112
|
+
this.setNodeProperty(nodeId, prop.key, prop.value);
|
|
1160
1113
|
}
|
|
1161
1114
|
for (const prop of transientProps) {
|
|
1162
|
-
this.transientPropertiesAndTheirOpIds.delete(`${prop.key}@${
|
|
1163
|
-
}
|
|
1164
|
-
|
|
1165
|
-
}
|
|
1166
|
-
|
|
1167
|
-
|
|
1168
|
-
if (v === void 0) return true;
|
|
1169
|
-
if (v === null) return true;
|
|
1170
|
-
const t = typeof v;
|
|
1171
|
-
if (t === "string" || t === "number" || t === "boolean") return true;
|
|
1172
|
-
if (t === "bigint" || t === "function" || t === "symbol") return false;
|
|
1173
|
-
if (Array.isArray(v)) return v.every(isJsonValue2);
|
|
1174
|
-
if (t === "object") {
|
|
1175
|
-
if (v instanceof Date) return false;
|
|
1176
|
-
if (v instanceof Map || v instanceof Set || v instanceof RegExp) return false;
|
|
1177
|
-
if (ArrayBuffer.isView(v)) return false;
|
|
1178
|
-
const proto = Object.getPrototypeOf(v);
|
|
1179
|
-
if (proto !== Object.prototype && proto !== null) return false;
|
|
1180
|
-
for (const val of Object.values(v)) {
|
|
1181
|
-
if (!isJsonValue2(val)) return false;
|
|
1182
|
-
}
|
|
1183
|
-
return true;
|
|
1184
|
-
}
|
|
1185
|
-
return false;
|
|
1186
|
-
};
|
|
1187
|
-
if (!isJsonValue2(value)) {
|
|
1115
|
+
this.transientPropertiesAndTheirOpIds.delete(`${prop.key}@${nodeId}`);
|
|
1116
|
+
}
|
|
1117
|
+
node.clearAllTransientProperties();
|
|
1118
|
+
}
|
|
1119
|
+
setNodeProperty(nodeId, key, value) {
|
|
1120
|
+
if (!isJsonValue(value)) {
|
|
1188
1121
|
throw new Error(`Unsupported property value for key "${key}"`);
|
|
1189
1122
|
}
|
|
1190
|
-
this.
|
|
1191
|
-
const op =
|
|
1123
|
+
this.propClock++;
|
|
1124
|
+
const op = newSetNodePropertyOp(this.propClock, this.peerId, nodeId, key, value);
|
|
1192
1125
|
this.localOps.push(op);
|
|
1193
1126
|
this.applyProperty(op);
|
|
1194
1127
|
}
|
|
1195
|
-
|
|
1128
|
+
setNodeProperties(nodeId, props) {
|
|
1196
1129
|
const typedProps = props;
|
|
1197
1130
|
for (const [key, value] of Object.entries(typedProps)) {
|
|
1198
|
-
this.
|
|
1131
|
+
this.setNodeProperty(nodeId, key, value);
|
|
1199
1132
|
}
|
|
1200
1133
|
}
|
|
1201
|
-
|
|
1134
|
+
getNodeByPath(path) {
|
|
1202
1135
|
path = path.replace(/^\/+/, "");
|
|
1203
1136
|
path = path.replace(/\/+$/, "");
|
|
1204
|
-
const
|
|
1205
|
-
if (!
|
|
1137
|
+
const root = this.root;
|
|
1138
|
+
if (!root) {
|
|
1206
1139
|
return void 0;
|
|
1207
1140
|
}
|
|
1208
|
-
|
|
1209
|
-
|
|
1210
|
-
throw new Error("The root vertex is not found");
|
|
1141
|
+
if (path === "") {
|
|
1142
|
+
return root;
|
|
1211
1143
|
}
|
|
1212
|
-
|
|
1213
|
-
return vertex;
|
|
1144
|
+
return this.getNodeByPathArray(root, path.split("/"));
|
|
1214
1145
|
}
|
|
1215
|
-
|
|
1146
|
+
getNodeByPathArray(node, path) {
|
|
1216
1147
|
if (path.length === 0) {
|
|
1217
|
-
return
|
|
1148
|
+
return node ?? void 0;
|
|
1218
1149
|
}
|
|
1219
1150
|
const targetName = path[0];
|
|
1220
|
-
const children = this.getChildren(
|
|
1151
|
+
const children = this.getChildren(node.id);
|
|
1221
1152
|
for (const child of children) {
|
|
1222
1153
|
if (child.getProperty("name") === targetName) {
|
|
1223
|
-
return this.
|
|
1154
|
+
return this.getNodeByPathArray(child, path.slice(1));
|
|
1224
1155
|
}
|
|
1225
1156
|
}
|
|
1226
1157
|
return void 0;
|
|
1227
1158
|
}
|
|
1228
1159
|
printTree() {
|
|
1229
|
-
if (!this.
|
|
1160
|
+
if (!this.rootNodeId) {
|
|
1230
1161
|
return "";
|
|
1231
1162
|
}
|
|
1232
|
-
return this.state.printTree(this.
|
|
1163
|
+
return this.state.printTree(this.rootNodeId);
|
|
1233
1164
|
}
|
|
1234
1165
|
merge(ops) {
|
|
1235
1166
|
this.applyOps(ops);
|
|
1236
1167
|
}
|
|
1237
1168
|
applyOps(ops) {
|
|
1238
|
-
|
|
1239
|
-
|
|
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))) {
|
|
1240
1179
|
continue;
|
|
1241
1180
|
}
|
|
1242
1181
|
this.applyOperation(op);
|
|
@@ -1244,7 +1183,7 @@ var _RepTree = class _RepTree {
|
|
|
1244
1183
|
}
|
|
1245
1184
|
/** Applies operations in an optimized way, sorting move ops by OpId to avoid undo-do-redo cycles */
|
|
1246
1185
|
applyOpsOptimizedForLotsOfMoves(ops) {
|
|
1247
|
-
const newMoveOps = ops.filter((op) =>
|
|
1186
|
+
const newMoveOps = ops.filter((op) => isMoveNodeOp(op) && !this.knownOps.has(this.getOpKey(op)));
|
|
1248
1187
|
if (newMoveOps.length > 0) {
|
|
1249
1188
|
const allMoveOps = [...this.moveOps, ...newMoveOps];
|
|
1250
1189
|
allMoveOps.sort((a, b) => compareOpId(a.id, b.id));
|
|
@@ -1253,7 +1192,7 @@ var _RepTree = class _RepTree {
|
|
|
1253
1192
|
this.applyMove(op);
|
|
1254
1193
|
}
|
|
1255
1194
|
}
|
|
1256
|
-
const propertyOps = ops.filter((op) => isAnyPropertyOp(op) && !this.knownOps.has(
|
|
1195
|
+
const propertyOps = ops.filter((op) => isAnyPropertyOp(op) && !this.knownOps.has(this.getOpKey(op)));
|
|
1257
1196
|
for (let i = 0, len = propertyOps.length; i < len; i++) {
|
|
1258
1197
|
const op = propertyOps[i];
|
|
1259
1198
|
this.applyProperty(op);
|
|
@@ -1263,10 +1202,10 @@ var _RepTree = class _RepTree {
|
|
|
1263
1202
|
if (this.root?.id !== other.root?.id) {
|
|
1264
1203
|
return false;
|
|
1265
1204
|
}
|
|
1266
|
-
if (!this.
|
|
1205
|
+
if (!this.rootNodeId) {
|
|
1267
1206
|
return true;
|
|
1268
1207
|
}
|
|
1269
|
-
return _RepTree.
|
|
1208
|
+
return _RepTree.compareNodes(this.rootNodeId, this, other);
|
|
1270
1209
|
}
|
|
1271
1210
|
compareMoveOps(other) {
|
|
1272
1211
|
const movesA = this.moveOps;
|
|
@@ -1284,65 +1223,65 @@ var _RepTree = class _RepTree {
|
|
|
1284
1223
|
/** Checks if the given `ancestorId` is an ancestor of `childId` in the tree */
|
|
1285
1224
|
isAncestor(childId, ancestorId) {
|
|
1286
1225
|
let targetId = childId;
|
|
1287
|
-
let
|
|
1288
|
-
const
|
|
1289
|
-
while (
|
|
1290
|
-
if (
|
|
1291
|
-
if (!
|
|
1292
|
-
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)) {
|
|
1293
1232
|
console.error(`isAncestor: cycle detected in the tree structure.`);
|
|
1294
1233
|
return false;
|
|
1295
1234
|
}
|
|
1296
|
-
|
|
1297
|
-
targetId =
|
|
1235
|
+
visitedNodes.add(targetId);
|
|
1236
|
+
targetId = node.parentId;
|
|
1298
1237
|
}
|
|
1299
1238
|
return false;
|
|
1300
1239
|
}
|
|
1301
|
-
|
|
1302
|
-
const
|
|
1303
|
-
if (
|
|
1304
|
-
callback(
|
|
1240
|
+
observeNode(nodeId, callback) {
|
|
1241
|
+
const node = this.getNode(nodeId);
|
|
1242
|
+
if (node) {
|
|
1243
|
+
callback(node);
|
|
1305
1244
|
}
|
|
1306
|
-
const unsubscribe = this.observe(
|
|
1307
|
-
const
|
|
1308
|
-
if (
|
|
1309
|
-
callback(
|
|
1245
|
+
const unsubscribe = this.observe(nodeId, (_) => {
|
|
1246
|
+
const node2 = this.getNode(nodeId);
|
|
1247
|
+
if (node2) {
|
|
1248
|
+
callback(node2);
|
|
1310
1249
|
}
|
|
1311
1250
|
});
|
|
1312
1251
|
return () => {
|
|
1313
1252
|
unsubscribe();
|
|
1314
1253
|
};
|
|
1315
1254
|
}
|
|
1316
|
-
|
|
1255
|
+
observeNodeMove(callback) {
|
|
1317
1256
|
const listener = (events) => {
|
|
1318
1257
|
const moveEvent = events.find((e) => e.type === "move");
|
|
1319
1258
|
if (moveEvent) {
|
|
1320
|
-
const
|
|
1321
|
-
if (
|
|
1322
|
-
callback(
|
|
1259
|
+
const node = this.getNode(moveEvent.nodeId);
|
|
1260
|
+
if (node) {
|
|
1261
|
+
callback(node, moveEvent.oldParentId === void 0);
|
|
1323
1262
|
}
|
|
1324
1263
|
}
|
|
1325
1264
|
};
|
|
1326
1265
|
this.state.addGlobalChangeCallback(listener);
|
|
1327
1266
|
return () => this.state.removeGlobalChangeCallback(listener);
|
|
1328
1267
|
}
|
|
1329
|
-
observe(
|
|
1330
|
-
this.state.addChangeCallback(
|
|
1331
|
-
return () => this.state.removeChangeCallback(
|
|
1268
|
+
observe(nodeId, callback) {
|
|
1269
|
+
this.state.addChangeCallback(nodeId, callback);
|
|
1270
|
+
return () => this.state.removeChangeCallback(nodeId, callback);
|
|
1332
1271
|
}
|
|
1333
1272
|
observeOpApplied(callback) {
|
|
1334
1273
|
this.opAppliedCallbacks.push(callback);
|
|
1335
1274
|
return () => this.opAppliedCallbacks = this.opAppliedCallbacks.filter((l) => l !== callback);
|
|
1336
1275
|
}
|
|
1337
|
-
static
|
|
1338
|
-
const childrenA = treeA.state.getChildrenIds(
|
|
1339
|
-
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);
|
|
1340
1279
|
if (childrenA.length !== childrenB.length) {
|
|
1341
1280
|
return false;
|
|
1342
1281
|
}
|
|
1343
|
-
if (
|
|
1344
|
-
const propertiesA = treeA.
|
|
1345
|
-
const propertiesB = treeB.
|
|
1282
|
+
if (nodeId !== null) {
|
|
1283
|
+
const propertiesA = treeA.getNodeProperties(nodeId);
|
|
1284
|
+
const propertiesB = treeB.getNodeProperties(nodeId);
|
|
1346
1285
|
if (propertiesA.length !== propertiesB.length) {
|
|
1347
1286
|
return false;
|
|
1348
1287
|
}
|
|
@@ -1356,39 +1295,44 @@ var _RepTree = class _RepTree {
|
|
|
1356
1295
|
if (!childrenB.includes(childId)) {
|
|
1357
1296
|
return false;
|
|
1358
1297
|
}
|
|
1359
|
-
if (!_RepTree.
|
|
1298
|
+
if (!_RepTree.compareNodes(childId, treeA, treeB)) {
|
|
1360
1299
|
return false;
|
|
1361
1300
|
}
|
|
1362
1301
|
}
|
|
1363
1302
|
return true;
|
|
1364
1303
|
}
|
|
1365
|
-
|
|
1366
|
-
this.
|
|
1367
|
-
const op =
|
|
1304
|
+
newNodeInternal(nodeId, parentId) {
|
|
1305
|
+
this.moveClock++;
|
|
1306
|
+
const op = newMoveNodeOp(this.moveClock, this.peerId, nodeId, parentId);
|
|
1368
1307
|
this.localOps.push(op);
|
|
1369
1308
|
this.applyMove(op);
|
|
1370
|
-
this.
|
|
1371
|
-
return
|
|
1309
|
+
this.setNodeProperty(nodeId, "_c", (/* @__PURE__ */ new Date()).toISOString());
|
|
1310
|
+
return nodeId;
|
|
1372
1311
|
}
|
|
1373
|
-
|
|
1374
|
-
const
|
|
1375
|
-
return this.
|
|
1312
|
+
newNodeInternalWithUUID(parentId) {
|
|
1313
|
+
const nodeId = uuid();
|
|
1314
|
+
return this.newNodeInternal(nodeId, parentId);
|
|
1376
1315
|
}
|
|
1377
|
-
|
|
1378
|
-
const
|
|
1379
|
-
if (this.state.
|
|
1316
|
+
ensureNullNode() {
|
|
1317
|
+
const nodeId = _RepTree.NULL_NODE_ID;
|
|
1318
|
+
if (this.state.getNode(nodeId)) {
|
|
1380
1319
|
return;
|
|
1381
1320
|
}
|
|
1382
|
-
this.
|
|
1321
|
+
this.newNodeInternal(nodeId, null);
|
|
1383
1322
|
}
|
|
1384
1323
|
/** Updates the lamport clock with the counter value of the operation */
|
|
1385
|
-
|
|
1386
|
-
if (operation.id.counter > this.
|
|
1387
|
-
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;
|
|
1388
1332
|
}
|
|
1389
1333
|
}
|
|
1390
1334
|
applyPendingMovesForParent(parentId) {
|
|
1391
|
-
if (!this.state.
|
|
1335
|
+
if (!this.state.getNode(parentId)) {
|
|
1392
1336
|
return;
|
|
1393
1337
|
}
|
|
1394
1338
|
const pendingMoves = this.pendingMovesWithMissingParent.get(parentId);
|
|
@@ -1401,14 +1345,15 @@ var _RepTree = class _RepTree {
|
|
|
1401
1345
|
}
|
|
1402
1346
|
}
|
|
1403
1347
|
applyMove(op) {
|
|
1404
|
-
if (op.parentId !== null && !this.state.
|
|
1348
|
+
if (op.parentId !== null && !this.state.getNode(op.parentId)) {
|
|
1405
1349
|
if (!this.pendingMovesWithMissingParent.has(op.parentId)) {
|
|
1406
1350
|
this.pendingMovesWithMissingParent.set(op.parentId, []);
|
|
1407
1351
|
}
|
|
1408
1352
|
this.pendingMovesWithMissingParent.get(op.parentId).push(op);
|
|
1353
|
+
this.markOpSeen(op, true);
|
|
1409
1354
|
return;
|
|
1410
1355
|
}
|
|
1411
|
-
this.
|
|
1356
|
+
this.updateMoveClock(op);
|
|
1412
1357
|
const lastOp = this.moveOps.length > 0 ? this.moveOps[this.moveOps.length - 1] : null;
|
|
1413
1358
|
if (lastOp === null || isOpIdGreaterThan(op.id, lastOp.id)) {
|
|
1414
1359
|
this.moveOps.push(op);
|
|
@@ -1435,130 +1380,138 @@ var _RepTree = class _RepTree {
|
|
|
1435
1380
|
this.applyPendingMovesForParent(op.targetId);
|
|
1436
1381
|
}
|
|
1437
1382
|
setLLWPropertyAndItsOpId(op) {
|
|
1438
|
-
this.
|
|
1383
|
+
this.propertyOpsByKey.set(`${op.key}@${op.targetId}`, op);
|
|
1439
1384
|
this.state.setProperty(op.targetId, op.key, op.value);
|
|
1440
|
-
this.reportOpAsApplied(op);
|
|
1385
|
+
this.reportOpAsApplied(op, false);
|
|
1386
|
+
this.refreshPropStateVector();
|
|
1441
1387
|
}
|
|
1442
1388
|
setTransientPropertyAndItsOpId(op) {
|
|
1443
1389
|
this.transientPropertiesAndTheirOpIds.set(`${op.key}@${op.targetId}`, op.id);
|
|
1444
1390
|
this.state.setTransientProperty(op.targetId, op.key, op.value);
|
|
1445
|
-
this.reportOpAsApplied(op);
|
|
1391
|
+
this.reportOpAsApplied(op, false);
|
|
1446
1392
|
}
|
|
1447
1393
|
applyProperty(op) {
|
|
1448
|
-
const
|
|
1449
|
-
if (!
|
|
1394
|
+
const targetNode = this.state.getNode(op.targetId);
|
|
1395
|
+
if (!targetNode) {
|
|
1450
1396
|
if (op.transient) {
|
|
1451
1397
|
return;
|
|
1452
1398
|
}
|
|
1453
|
-
if (!this.
|
|
1454
|
-
this.
|
|
1399
|
+
if (!this.pendingPropertiesWithMissingNode.has(op.targetId)) {
|
|
1400
|
+
this.pendingPropertiesWithMissingNode.set(op.targetId, []);
|
|
1455
1401
|
}
|
|
1456
|
-
this.
|
|
1402
|
+
this.pendingPropertiesWithMissingNode.get(op.targetId).push(op);
|
|
1403
|
+
this.markOpSeen(op, false);
|
|
1457
1404
|
return;
|
|
1458
1405
|
}
|
|
1459
|
-
this.
|
|
1460
|
-
this.applyLLWProperty(op,
|
|
1406
|
+
this.updatePropClock(op);
|
|
1407
|
+
this.applyLLWProperty(op, targetNode);
|
|
1461
1408
|
}
|
|
1462
|
-
applyLLWProperty(op,
|
|
1409
|
+
applyLLWProperty(op, targetNode) {
|
|
1463
1410
|
const prevTransientOpId = this.transientPropertiesAndTheirOpIds.get(`${op.key}@${op.targetId}`);
|
|
1464
|
-
const prevOpId = this.
|
|
1411
|
+
const prevOpId = this.propertyOpsByKey.get(`${op.key}@${op.targetId}`)?.id;
|
|
1465
1412
|
if (!op.transient) {
|
|
1466
|
-
this.setPropertyOps.push(op);
|
|
1467
1413
|
if (!prevOpId || isOpIdGreaterThan(op.id, prevOpId)) {
|
|
1468
1414
|
this.setLLWPropertyAndItsOpId(op);
|
|
1469
1415
|
} else {
|
|
1470
|
-
this.
|
|
1416
|
+
this.markOpSeen(op, false);
|
|
1471
1417
|
}
|
|
1472
1418
|
if (prevTransientOpId && isOpIdGreaterThan(op.id, prevTransientOpId)) {
|
|
1473
1419
|
this.transientPropertiesAndTheirOpIds.delete(`${op.key}@${op.targetId}`);
|
|
1474
|
-
|
|
1420
|
+
targetNode.removeTransientProperty(op.key);
|
|
1475
1421
|
}
|
|
1476
1422
|
} else {
|
|
1477
1423
|
if (!prevTransientOpId || isOpIdGreaterThan(op.id, prevTransientOpId)) {
|
|
1478
1424
|
this.setTransientPropertyAndItsOpId(op);
|
|
1425
|
+
} else {
|
|
1426
|
+
this.markOpSeen(op, false);
|
|
1479
1427
|
}
|
|
1480
1428
|
}
|
|
1481
1429
|
}
|
|
1482
1430
|
applyOperation(op) {
|
|
1483
|
-
if (
|
|
1431
|
+
if (isMoveNodeOp(op)) {
|
|
1484
1432
|
this.applyMove(op);
|
|
1485
1433
|
} else if (isAnyPropertyOp(op)) {
|
|
1486
1434
|
this.applyProperty(op);
|
|
1487
1435
|
}
|
|
1488
1436
|
}
|
|
1489
|
-
|
|
1490
|
-
this.knownOps.add(
|
|
1491
|
-
if (this._stateVectorEnabled) {
|
|
1492
|
-
|
|
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
|
+
}
|
|
1493
1445
|
}
|
|
1446
|
+
}
|
|
1447
|
+
reportOpAsApplied(op, includeInStateVector = true) {
|
|
1448
|
+
this.markOpSeen(op, includeInStateVector);
|
|
1494
1449
|
for (const callback of this.opAppliedCallbacks) {
|
|
1495
1450
|
callback(op);
|
|
1496
1451
|
}
|
|
1497
1452
|
}
|
|
1498
1453
|
tryToMove(op) {
|
|
1499
|
-
let
|
|
1500
|
-
if (
|
|
1501
|
-
this.parentIdBeforeMove.set(op.id,
|
|
1454
|
+
let targetNode = this.state.getNode(op.targetId);
|
|
1455
|
+
if (targetNode) {
|
|
1456
|
+
this.parentIdBeforeMove.set(op.id, targetNode.parentId);
|
|
1502
1457
|
}
|
|
1503
1458
|
if (op.targetId === op.parentId) return;
|
|
1504
1459
|
if (op.parentId && this.isAncestor(op.parentId, op.targetId)) return;
|
|
1505
|
-
this.state.
|
|
1506
|
-
if (!
|
|
1507
|
-
const pendingProperties = this.
|
|
1508
|
-
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);
|
|
1509
1464
|
for (const prop of pendingProperties) {
|
|
1510
1465
|
this.applyProperty(prop);
|
|
1511
1466
|
}
|
|
1512
1467
|
}
|
|
1513
1468
|
}
|
|
1514
1469
|
undoMove(op) {
|
|
1515
|
-
const
|
|
1516
|
-
if (!
|
|
1517
|
-
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`);
|
|
1518
1473
|
return;
|
|
1519
1474
|
}
|
|
1520
1475
|
const prevParentId = this.parentIdBeforeMove.get(op.id);
|
|
1521
1476
|
if (prevParentId === void 0) {
|
|
1522
1477
|
return;
|
|
1523
1478
|
}
|
|
1524
|
-
this.state.
|
|
1479
|
+
this.state.moveNode(op.targetId, prevParentId);
|
|
1525
1480
|
}
|
|
1526
|
-
// --- Range-Based State Vector Methods ---
|
|
1481
|
+
// --- Range-Based State Vector Methods ---
|
|
1527
1482
|
/**
|
|
1528
|
-
* Returns the current state
|
|
1529
|
-
* Returns
|
|
1483
|
+
* Returns the current state vectors for move and property streams.
|
|
1484
|
+
* Returns readonly references to the internal state vectors.
|
|
1530
1485
|
*/
|
|
1531
|
-
|
|
1486
|
+
getStateVectors() {
|
|
1532
1487
|
if (!this._stateVectorEnabled) {
|
|
1533
1488
|
return null;
|
|
1534
1489
|
}
|
|
1535
|
-
return
|
|
1490
|
+
return {
|
|
1491
|
+
move: this.moveStateVector.getState(),
|
|
1492
|
+
prop: this.propStateVector.getState()
|
|
1493
|
+
};
|
|
1536
1494
|
}
|
|
1537
1495
|
/**
|
|
1538
|
-
* Determines which operations are needed to synchronize
|
|
1496
|
+
* Determines which operations are needed to synchronize
|
|
1539
1497
|
* with the provided state vector.
|
|
1540
|
-
*
|
|
1541
|
-
* @param
|
|
1542
|
-
* @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.
|
|
1543
1501
|
*/
|
|
1544
|
-
getMissingOps(
|
|
1502
|
+
getMissingOps(theirStateVectors) {
|
|
1545
1503
|
if (!this._stateVectorEnabled) {
|
|
1546
|
-
return [...this.moveOps, ...this.
|
|
1547
|
-
}
|
|
1548
|
-
const
|
|
1549
|
-
const
|
|
1550
|
-
const
|
|
1551
|
-
const
|
|
1552
|
-
|
|
1553
|
-
|
|
1554
|
-
|
|
1555
|
-
|
|
1556
|
-
|
|
1557
|
-
}
|
|
1558
|
-
}
|
|
1559
|
-
}
|
|
1560
|
-
missingOps.sort((a, b) => compareOpId(a.id, b.id));
|
|
1561
|
-
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];
|
|
1562
1515
|
}
|
|
1563
1516
|
/**
|
|
1564
1517
|
* Gets or sets whether state vector tracking is enabled
|
|
@@ -1574,43 +1527,70 @@ var _RepTree = class _RepTree {
|
|
|
1574
1527
|
if (value === this._stateVectorEnabled) return;
|
|
1575
1528
|
if (value) {
|
|
1576
1529
|
this._stateVectorEnabled = true;
|
|
1577
|
-
this.
|
|
1530
|
+
this.moveStateVector = StateVector.fromOperations(this.moveOps);
|
|
1531
|
+
this.propStateVector = StateVector.fromOperations(this.getPropertyOps());
|
|
1578
1532
|
} else {
|
|
1579
1533
|
this._stateVectorEnabled = false;
|
|
1580
|
-
this.
|
|
1534
|
+
this.moveStateVector = new StateVector();
|
|
1535
|
+
this.propStateVector = new StateVector();
|
|
1581
1536
|
}
|
|
1582
1537
|
}
|
|
1583
1538
|
/**
|
|
1584
|
-
* Parses the
|
|
1539
|
+
* Parses the node properties with a provided schema that has a `parse` method (e.g., Zod schema)
|
|
1585
1540
|
*/
|
|
1586
|
-
|
|
1587
|
-
const propsArray = this.
|
|
1541
|
+
parseNode(nodeId, schema) {
|
|
1542
|
+
const propsArray = this.getNodeProperties(nodeId);
|
|
1588
1543
|
const propsObject = {};
|
|
1589
1544
|
for (const { key, value } of propsArray) {
|
|
1590
1545
|
propsObject[key] = value;
|
|
1591
1546
|
}
|
|
1592
1547
|
return schema.parse(propsObject);
|
|
1593
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
|
+
}
|
|
1594
1574
|
};
|
|
1595
|
-
_RepTree.
|
|
1575
|
+
_RepTree.NULL_NODE_ID = "0";
|
|
1596
1576
|
var RepTree = _RepTree;
|
|
1597
1577
|
// Annotate the CommonJS export names for ESM import in node:
|
|
1598
1578
|
0 && (module.exports = {
|
|
1579
|
+
Node,
|
|
1580
|
+
NodeState,
|
|
1599
1581
|
RepTree,
|
|
1600
1582
|
StateVector,
|
|
1601
1583
|
TreeState,
|
|
1602
|
-
|
|
1603
|
-
VertexState,
|
|
1604
|
-
bindVertex,
|
|
1584
|
+
bindNode,
|
|
1605
1585
|
compareOpId,
|
|
1606
1586
|
createOpId,
|
|
1607
1587
|
equalsOpId,
|
|
1608
1588
|
isAnyPropertyOp,
|
|
1609
|
-
|
|
1589
|
+
isMoveNodeOp,
|
|
1610
1590
|
isOpIdGreaterThan,
|
|
1611
|
-
|
|
1612
|
-
|
|
1613
|
-
|
|
1591
|
+
newMoveNodeOp,
|
|
1592
|
+
newSetNodePropertyOp,
|
|
1593
|
+
newSetTransientNodePropertyOp,
|
|
1614
1594
|
opIdToString,
|
|
1615
1595
|
tryParseOpIdStr,
|
|
1616
1596
|
uuid
|