reptree 0.1.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/dist/index.js ADDED
@@ -0,0 +1,942 @@
1
+ // src/OpId.ts
2
+ var OpId = class _OpId {
3
+ constructor(counter, peerId) {
4
+ this.counter = counter;
5
+ this.peerId = peerId;
6
+ }
7
+ static compare(opIdA, opIdB) {
8
+ if (!(opIdA instanceof _OpId)) {
9
+ const parsedA = _OpId.tryParseStr(opIdA);
10
+ if (!parsedA) throw new Error(`Invalid OpId string: ${opIdA}`);
11
+ opIdA = parsedA;
12
+ }
13
+ if (!(opIdB instanceof _OpId)) {
14
+ const parsedB = _OpId.tryParseStr(opIdB);
15
+ if (!parsedB) throw new Error(`Invalid OpId string: ${opIdB}`);
16
+ opIdB = parsedB;
17
+ }
18
+ const counterA = opIdA.counter;
19
+ const counterB = opIdB.counter;
20
+ if (counterA > counterB) {
21
+ return 1;
22
+ } else if (counterA < counterB) {
23
+ return -1;
24
+ } else {
25
+ return opIdA.peerId.localeCompare(opIdB.peerId);
26
+ }
27
+ }
28
+ static equals(opIdA, opIdB) {
29
+ if (opIdA === opIdB) {
30
+ return true;
31
+ } else if (!opIdA || !opIdB) {
32
+ return false;
33
+ }
34
+ return _OpId.compare(opIdA, opIdB) === 0;
35
+ }
36
+ static tryParseStr(opIdStr) {
37
+ const parts = opIdStr.split("@");
38
+ if (parts.length !== 2) {
39
+ throw new Error(`Invalid OpId string: ${opIdStr}`);
40
+ }
41
+ return new _OpId(parseInt(parts[0], 10), parts[1]);
42
+ }
43
+ isGreaterThan(opId) {
44
+ return _OpId.compare(this, opId) === 1;
45
+ }
46
+ toString() {
47
+ return `${this.counter}@${this.peerId}`;
48
+ }
49
+ };
50
+
51
+ // src/operations.ts
52
+ function isMoveVertexOp(op) {
53
+ return "parentId" in op;
54
+ }
55
+ function isSetPropertyOp(op) {
56
+ return "key" in op;
57
+ }
58
+ function newMoveVertexOp(clock, peerId, targetId, parentId) {
59
+ return { id: new OpId(clock, peerId), targetId, parentId };
60
+ }
61
+ function newSetVertexPropertyOp(clock, peerId, targetId, key, value) {
62
+ return { id: new OpId(clock, peerId), targetId, key, value, transient: false };
63
+ }
64
+ function newSetTransientVertexPropertyOp(clock, peerId, targetId, key, value) {
65
+ return { id: new OpId(clock, peerId), targetId, key, value, transient: true };
66
+ }
67
+
68
+ // src/VertexState.ts
69
+ var VertexState = class {
70
+ constructor(id, parentId) {
71
+ this.id = id;
72
+ this.parentId = parentId;
73
+ this.properties = [];
74
+ this.transientProperties = [];
75
+ this.children = [];
76
+ }
77
+ setProperty(key, value) {
78
+ const existingPropIndex = this.properties.findIndex((p) => p.key === key);
79
+ if (existingPropIndex !== -1) {
80
+ if (value !== void 0) {
81
+ this.properties[existingPropIndex] = { key, value };
82
+ } else {
83
+ this.removeProperty(key);
84
+ }
85
+ } else {
86
+ if (value !== void 0) {
87
+ this.properties.push({ key, value });
88
+ }
89
+ }
90
+ }
91
+ setTransientProperty(key, value) {
92
+ const existingPropIndex = this.transientProperties.findIndex((p) => p.key === key);
93
+ if (existingPropIndex !== -1) {
94
+ if (value !== void 0) {
95
+ this.transientProperties[existingPropIndex] = { key, value };
96
+ } else {
97
+ this.removeTransientProperty(key);
98
+ }
99
+ } else {
100
+ if (value !== void 0) {
101
+ this.transientProperties.push({ key, value });
102
+ }
103
+ }
104
+ }
105
+ getProperty(key, includingTransient = true) {
106
+ if (includingTransient) {
107
+ const transientProp = this.transientProperties.find((p) => p.key === key);
108
+ if (transientProp) {
109
+ return transientProp.value;
110
+ }
111
+ }
112
+ return this.properties.find((p) => p.key === key)?.value;
113
+ }
114
+ getAllProperties(includingTransient = true) {
115
+ if (!includingTransient) {
116
+ return this.properties;
117
+ }
118
+ const result = [];
119
+ const seenKeys = /* @__PURE__ */ new Set();
120
+ for (const prop of this.transientProperties) {
121
+ result.push(prop);
122
+ seenKeys.add(prop.key);
123
+ }
124
+ for (const prop of this.properties) {
125
+ if (!seenKeys.has(prop.key)) {
126
+ result.push(prop);
127
+ }
128
+ }
129
+ return result;
130
+ }
131
+ removeProperty(key) {
132
+ this.properties = this.properties.filter((p) => p.key !== key);
133
+ }
134
+ removeTransientProperty(key) {
135
+ this.transientProperties = this.transientProperties.filter((p) => p.key !== key);
136
+ }
137
+ };
138
+
139
+ // src/TreeState.ts
140
+ var TreeState = class {
141
+ constructor() {
142
+ this.changeCallbacks = /* @__PURE__ */ new Map();
143
+ this.globalChangeCallbacks = /* @__PURE__ */ new Set();
144
+ this.batchedEvents = /* @__PURE__ */ new Map();
145
+ this.vertices = /* @__PURE__ */ new Map();
146
+ this.batchTickInterval = setInterval(() => {
147
+ this.processBatchedEvents();
148
+ }, 33.3);
149
+ }
150
+ dispose() {
151
+ clearInterval(this.batchTickInterval);
152
+ }
153
+ processBatchedEvents() {
154
+ for (const [vertexId, events] of this.batchedEvents) {
155
+ let lastMoveEvent = null;
156
+ let lastChildrenEvent = null;
157
+ const propertyEventsByKey = /* @__PURE__ */ new Map();
158
+ for (let i = events.length - 1; i >= 0; i--) {
159
+ const event = events[i];
160
+ if (!lastMoveEvent && event.type === "move") lastMoveEvent = event;
161
+ if (!lastChildrenEvent && event.type === "children") lastChildrenEvent = event;
162
+ if (event.type === "property") {
163
+ const propertyEvent = event;
164
+ if (!propertyEventsByKey.has(propertyEvent.key)) {
165
+ propertyEventsByKey.set(propertyEvent.key, propertyEvent);
166
+ }
167
+ }
168
+ }
169
+ const filteredEvents = [
170
+ ...lastMoveEvent ? [lastMoveEvent] : [],
171
+ ...lastChildrenEvent ? [lastChildrenEvent] : [],
172
+ ...propertyEventsByKey.values()
173
+ ];
174
+ this.globalChangeCallbacks.forEach((listener) => listener(filteredEvents));
175
+ this.changeCallbacks.get(vertexId)?.forEach((listener) => listener(filteredEvents));
176
+ }
177
+ this.batchedEvents.clear();
178
+ }
179
+ getAllVertices() {
180
+ return Array.from(this.vertices.values());
181
+ }
182
+ getVertex(id) {
183
+ return this.vertices.get(id);
184
+ }
185
+ getChildrenIds(vertexId) {
186
+ return this.getVertex(vertexId)?.children ?? [];
187
+ }
188
+ getChildren(vertexId) {
189
+ return this.getChildrenIds(vertexId).map((id) => {
190
+ const vertex = this.vertices.get(id);
191
+ return vertex ? vertex : void 0;
192
+ }).filter((vertex) => vertex !== void 0).sort((a, b) => {
193
+ const aDate = a.getProperty("_c");
194
+ const bDate = b.getProperty("_c");
195
+ if (!aDate) return -1;
196
+ if (!bDate) return 1;
197
+ return new Date(aDate).getTime() - new Date(bDate).getTime();
198
+ });
199
+ }
200
+ moveVertex(vertexId, newParentId) {
201
+ let vertex = this.getVertex(vertexId);
202
+ const prevParentId = vertex ? vertex.parentId : void 0;
203
+ if (!vertex) {
204
+ vertex = new VertexState(vertexId, newParentId);
205
+ this.vertices.set(vertexId, vertex);
206
+ }
207
+ if (prevParentId === newParentId) {
208
+ return vertex;
209
+ }
210
+ vertex.parentId = newParentId;
211
+ let childrenInNewParent = null;
212
+ let childrenInOldParent = null;
213
+ if (prevParentId) {
214
+ const oldParentVertex = this.getVertex(prevParentId);
215
+ if (oldParentVertex) {
216
+ oldParentVertex.children = oldParentVertex.children.filter((child) => child !== vertexId);
217
+ childrenInOldParent = oldParentVertex.children;
218
+ } else {
219
+ console.error(`Old parent vertex not found for ${prevParentId}`);
220
+ }
221
+ }
222
+ if (newParentId !== null) {
223
+ const newParentVertex = this.vertices.get(newParentId);
224
+ if (newParentVertex) {
225
+ newParentVertex.children.push(vertexId);
226
+ childrenInNewParent = newParentVertex.children;
227
+ } else {
228
+ console.error(`New parent vertex not found for ${newParentId}`);
229
+ }
230
+ }
231
+ this.notifyChange({
232
+ type: "move",
233
+ vertexId,
234
+ oldParentId: prevParentId,
235
+ newParentId
236
+ });
237
+ if (childrenInNewParent !== null && newParentId !== null) {
238
+ this.notifyChange({
239
+ type: "children",
240
+ vertexId: newParentId,
241
+ children: childrenInNewParent.map((id) => this.vertices.get(id))
242
+ });
243
+ }
244
+ if (childrenInOldParent !== null && prevParentId) {
245
+ this.notifyChange({
246
+ type: "children",
247
+ vertexId: prevParentId,
248
+ children: childrenInOldParent.map((id) => this.vertices.get(id))
249
+ });
250
+ }
251
+ return vertex;
252
+ }
253
+ setProperty(vertexId, key, value) {
254
+ const vertex = this.getVertex(vertexId);
255
+ if (!vertex) {
256
+ throw new Error(`Vertex ${vertexId} not found`);
257
+ }
258
+ vertex.setProperty(key, value);
259
+ this.notifyChange({
260
+ type: "property",
261
+ vertexId,
262
+ key,
263
+ value
264
+ });
265
+ if (vertex.parentId !== null) {
266
+ this.notifyChange({
267
+ type: "children",
268
+ vertexId: vertex.parentId,
269
+ children: [vertex]
270
+ // @TODO: shoulld I set all children or rename this property?
271
+ });
272
+ }
273
+ }
274
+ setTransientProperty(vertexId, key, value) {
275
+ const vertex = this.getVertex(vertexId);
276
+ if (vertex) {
277
+ vertex.setTransientProperty(key, value);
278
+ }
279
+ this.notifyChange({
280
+ type: "property",
281
+ vertexId,
282
+ key,
283
+ value
284
+ });
285
+ }
286
+ addChangeCallback(vertexId, listener) {
287
+ if (!this.changeCallbacks.has(vertexId)) {
288
+ this.changeCallbacks.set(vertexId, /* @__PURE__ */ new Set());
289
+ }
290
+ this.changeCallbacks.get(vertexId).add(listener);
291
+ }
292
+ removeChangeCallback(vertexId, listener) {
293
+ this.changeCallbacks.get(vertexId)?.delete(listener);
294
+ }
295
+ addGlobalChangeCallback(listener) {
296
+ this.globalChangeCallbacks.add(listener);
297
+ }
298
+ removeGlobalChangeCallback(listener) {
299
+ this.globalChangeCallbacks.delete(listener);
300
+ }
301
+ notifyChange(event) {
302
+ let events = this.batchedEvents.get(event.vertexId);
303
+ if (!events) {
304
+ events = [];
305
+ this.batchedEvents.set(event.vertexId, events);
306
+ }
307
+ events.push(event);
308
+ }
309
+ printTree(vertexId, indent = "", isLast = true) {
310
+ const prefix = indent + (isLast ? "\u2514\u2500\u2500 " : "\u251C\u2500\u2500 ");
311
+ let result = prefix + vertexId + "\n";
312
+ let vertexName = null;
313
+ if (vertexId !== null) {
314
+ const vertex = this.getVertex(vertexId);
315
+ if (vertex) {
316
+ for (const prop of vertex.getAllProperties()) {
317
+ if (prop.key === "_n") {
318
+ vertexName = prop.value;
319
+ }
320
+ const propPrefix = indent + (isLast ? " " : "\u2502 ") + "\u2022 ";
321
+ result += `${propPrefix}${prop.key}: ${JSON.stringify(prop.value)}
322
+ `;
323
+ }
324
+ }
325
+ }
326
+ const children = this.getChildrenIds(vertexId);
327
+ for (let i = 0; i < children.length; i++) {
328
+ const childId = children[i];
329
+ const isLastChild = i === children.length - 1;
330
+ result += this.printTree(childId, indent + (isLast ? " " : "\u2502 "), isLastChild);
331
+ }
332
+ return result;
333
+ }
334
+ };
335
+
336
+ // src/uuid.ts
337
+ var removeDashes = (guid) => guid.replace(/-/g, "");
338
+ function uuid() {
339
+ return removeDashes(crypto.randomUUID());
340
+ }
341
+
342
+ // src/Vertex.ts
343
+ var Vertex = class {
344
+ constructor(tree, state) {
345
+ this.tree = tree;
346
+ this.state = state;
347
+ }
348
+ get id() {
349
+ return this.state.id;
350
+ }
351
+ get name() {
352
+ return this.getProperty("_n");
353
+ }
354
+ set name(name) {
355
+ this.tree.setVertexProperty(this.id, "_n", name);
356
+ }
357
+ get createdAt() {
358
+ const createdAt = this.getProperty("_c");
359
+ if (!createdAt) {
360
+ return /* @__PURE__ */ new Date(0);
361
+ }
362
+ return new Date(createdAt);
363
+ }
364
+ get path() {
365
+ throw new Error("Not implemented");
366
+ }
367
+ get parentId() {
368
+ return this.state.parentId;
369
+ }
370
+ get parent() {
371
+ if (!this.parentId) {
372
+ return void 0;
373
+ }
374
+ return this.tree.getVertex(this.parentId);
375
+ }
376
+ get children() {
377
+ return this.tree.getChildren(this.id);
378
+ }
379
+ get childrenIds() {
380
+ return this.tree.getChildrenIds(this.id);
381
+ }
382
+ getAsTypedObject() {
383
+ return this.getProperties();
384
+ }
385
+ getChildrenAsTypedArray() {
386
+ return this.children.map((v) => v.getAsTypedObject());
387
+ }
388
+ newChild(props) {
389
+ const typedProps = props;
390
+ return this.tree.newVertex(this.id, typedProps);
391
+ }
392
+ newNamedChild(name, props) {
393
+ const typedProps = props;
394
+ return this.tree.newNamedVertex(this.id, name, typedProps);
395
+ }
396
+ setProperty(key, value) {
397
+ const existingValue = this.getProperty(key, false);
398
+ if (existingValue === value) {
399
+ return;
400
+ }
401
+ this.tree.setVertexProperty(this.id, key, value);
402
+ }
403
+ setTransientProperty(key, value) {
404
+ const existingValue = this.getProperty(key);
405
+ if (existingValue === value) {
406
+ return;
407
+ }
408
+ this.tree.setTransientVertexProperty(this.id, key, value);
409
+ }
410
+ setProperties(props) {
411
+ for (const [key, value] of Object.entries(props)) {
412
+ this.setProperty(key, value);
413
+ }
414
+ }
415
+ getProperty(key, includingTransient = true) {
416
+ return this.tree.getVertexProperty(this.id, key, includingTransient);
417
+ }
418
+ getProperties() {
419
+ const props = {};
420
+ this.tree.getVertexProperties(this.id).forEach((p) => {
421
+ props[p.key] = p.value;
422
+ });
423
+ return props;
424
+ }
425
+ findAllChildrenWithProperty(key, value) {
426
+ return this.children.filter((c) => c.getProperty(key) === value);
427
+ }
428
+ findFirstChildVertexWithProperty(key, value) {
429
+ return this.children.find((c) => c.getProperty(key) === value);
430
+ }
431
+ findFirstTypedChildWithProperty(key, value) {
432
+ return this.findFirstChildVertexWithProperty(key, value)?.getAsTypedObject();
433
+ }
434
+ findAllTypedChildrenWithProperty(key, value) {
435
+ return this.findAllChildrenWithProperty(key, value).map((c) => c.getAsTypedObject());
436
+ }
437
+ observe(listener) {
438
+ const unobserve = this.tree.observe(this.id, listener);
439
+ return () => unobserve();
440
+ }
441
+ observeChildren(listener) {
442
+ const unobserve = this.tree.observe(this.id, (events) => {
443
+ if (events.some((e) => e.type === "children")) {
444
+ listener(this.children);
445
+ }
446
+ });
447
+ return () => unobserve();
448
+ }
449
+ observeChildrenAsTypedArray(listener) {
450
+ return this.observeChildren((children) => {
451
+ listener(children.map((c) => c.getProperties()));
452
+ });
453
+ }
454
+ delete() {
455
+ this.tree.deleteVertex(this.id);
456
+ }
457
+ moveTo(parent) {
458
+ this.tree.moveVertex(this.id, parent.id);
459
+ }
460
+ };
461
+
462
+ // src/RepTree.ts
463
+ var _RepTree = class _RepTree {
464
+ /**
465
+ * @param peerId - The peer ID of the current client
466
+ * @param ops - The operations to replicate an existing tree, if null - a new tree will be created
467
+ */
468
+ constructor(peerId, ops = null) {
469
+ this.lamportClock = 0;
470
+ this.moveOps = [];
471
+ this.setPropertyOps = [];
472
+ this.propertiesAndTheirOpIds = /* @__PURE__ */ new Map();
473
+ this.transientPropertiesAndTheirOpIds = /* @__PURE__ */ new Map();
474
+ this.localOps = [];
475
+ this.pendingMovesWithMissingParent = /* @__PURE__ */ new Map();
476
+ this.pendingPropertiesWithMissingVertex = /* @__PURE__ */ new Map();
477
+ this.appliedOps = /* @__PURE__ */ new Set();
478
+ this.parentIdBeforeMove = /* @__PURE__ */ new Map();
479
+ this.opAppliedCallbacks = [];
480
+ this.maxDepth = _RepTree.DEFAULT_MAX_DEPTH;
481
+ this.peerId = peerId;
482
+ this.state = new TreeState();
483
+ if (ops != null && ops.length > 0) {
484
+ let rootMoveOp;
485
+ for (let i = 0; i < ops.length; i++) {
486
+ if (isMoveVertexOp(ops[i]) && ops[i].parentId === null) {
487
+ rootMoveOp = ops[i];
488
+ break;
489
+ }
490
+ }
491
+ if (rootMoveOp) {
492
+ this.rootVertexId = rootMoveOp.targetId;
493
+ } else {
494
+ throw new Error("The operations has to contain a move operation with a parentId as null to set the root vertex");
495
+ }
496
+ this.applyOps(ops);
497
+ this.ensureTrashVertex();
498
+ } else {
499
+ this.rootVertexId = this.newVertexInternalWithUUID(null);
500
+ this.ensureTrashVertex();
501
+ }
502
+ }
503
+ getMoveOps() {
504
+ return this.moveOps;
505
+ }
506
+ getAllOps() {
507
+ return [...this.moveOps, ...this.setPropertyOps];
508
+ }
509
+ getVertex(vertexId) {
510
+ const vertex = this.state.getVertex(vertexId);
511
+ return vertex ? new Vertex(this, vertex) : void 0;
512
+ }
513
+ get rootVertex() {
514
+ const rootVertex = this.state.getVertex(this.rootVertexId);
515
+ if (!rootVertex) {
516
+ throw new Error("Root vertex not found");
517
+ }
518
+ return new Vertex(this, rootVertex);
519
+ }
520
+ getAllVertices() {
521
+ return this.state.getAllVertices().map((v) => new Vertex(this, v));
522
+ }
523
+ getParent(vertexId) {
524
+ const parentId = this.state.getVertex(vertexId)?.parentId;
525
+ const parent = parentId ? this.state.getVertex(parentId) : void 0;
526
+ return parent ? new Vertex(this, parent) : void 0;
527
+ }
528
+ getChildren(vertexId) {
529
+ return this.state.getChildren(vertexId).map((v) => new Vertex(this, v));
530
+ }
531
+ getChildrenIds(vertexId) {
532
+ return this.state.getChildrenIds(vertexId);
533
+ }
534
+ getAncestors(vertexId) {
535
+ const ancestors = [];
536
+ let currentVertex = this.state.getVertex(vertexId);
537
+ while (currentVertex && currentVertex.parentId) {
538
+ const parentVertex = this.state.getVertex(currentVertex.parentId);
539
+ if (parentVertex) {
540
+ ancestors.push(new Vertex(this, parentVertex));
541
+ currentVertex = parentVertex;
542
+ } else {
543
+ break;
544
+ }
545
+ }
546
+ return ancestors;
547
+ }
548
+ getVertexProperty(vertexId, key, includingTransient = true) {
549
+ const vertex = this.state.getVertex(vertexId);
550
+ if (!vertex) {
551
+ return void 0;
552
+ }
553
+ return vertex.getProperty(key, includingTransient);
554
+ }
555
+ getVertexProperties(vertexId) {
556
+ const vertex = this.state.getVertex(vertexId);
557
+ if (!vertex) {
558
+ return [];
559
+ }
560
+ return vertex.getAllProperties();
561
+ }
562
+ popLocalOps() {
563
+ const ops = this.localOps;
564
+ this.localOps = [];
565
+ return ops;
566
+ }
567
+ setMaxDepth(maxDepth) {
568
+ this.maxDepth = maxDepth;
569
+ }
570
+ newVertex(parentId, props = null) {
571
+ const typedProps = props;
572
+ const vertexId = this.newVertexInternalWithUUID(parentId);
573
+ if (typedProps) {
574
+ this.setVertexProperties(vertexId, typedProps);
575
+ }
576
+ const vertex = this.state.getVertex(vertexId);
577
+ if (!vertex) {
578
+ throw new Error("Failed to create vertex");
579
+ }
580
+ return new Vertex(this, vertex);
581
+ }
582
+ newNamedVertex(parentId, name, props = null) {
583
+ const typedProps = props;
584
+ const vertexId = this.newVertexInternalWithUUID(parentId);
585
+ if (typedProps) {
586
+ this.setVertexProperties(vertexId, typedProps);
587
+ }
588
+ this.setVertexProperty(vertexId, "_n", name);
589
+ const vertex = this.state.getVertex(vertexId);
590
+ if (!vertex) {
591
+ throw new Error("Failed to create named vertex");
592
+ }
593
+ return new Vertex(this, vertex);
594
+ }
595
+ moveVertex(vertexId, parentId) {
596
+ this.lamportClock++;
597
+ const op = newMoveVertexOp(this.lamportClock, this.peerId, vertexId, parentId);
598
+ this.localOps.push(op);
599
+ this.applyMove(op);
600
+ }
601
+ deleteVertex(vertexId) {
602
+ this.moveVertex(vertexId, _RepTree.TRASH_VERTEX_ID);
603
+ }
604
+ setTransientVertexProperty(vertexId, key, value) {
605
+ this.lamportClock++;
606
+ const op = newSetTransientVertexPropertyOp(this.lamportClock, this.peerId, vertexId, key, value);
607
+ this.localOps.push(op);
608
+ this.applyProperty(op);
609
+ }
610
+ setVertexProperty(vertexId, key, value) {
611
+ this.lamportClock++;
612
+ const op = newSetVertexPropertyOp(this.lamportClock, this.peerId, vertexId, key, value);
613
+ this.localOps.push(op);
614
+ this.applyProperty(op);
615
+ }
616
+ setVertexProperties(vertexId, props) {
617
+ const typedProps = props;
618
+ for (const [key, value] of Object.entries(typedProps)) {
619
+ this.setVertexProperty(vertexId, key, value);
620
+ }
621
+ }
622
+ getVertexByPath(path) {
623
+ path = path.replace(/^\/+/, "");
624
+ path = path.replace(/\/+$/, "");
625
+ const pathParts = path.split("/");
626
+ const root = this.state.getVertex(this.rootVertexId);
627
+ if (!root) {
628
+ throw new Error("The root vertex is not found");
629
+ }
630
+ const vertex = this.getVertexByPathArray(new Vertex(this, root), pathParts);
631
+ return vertex;
632
+ }
633
+ getVertexByPathArray(vertex, path) {
634
+ if (path.length === 0) {
635
+ return vertex ?? void 0;
636
+ }
637
+ const targetName = path[0];
638
+ const children = this.getChildren(vertex.id);
639
+ for (const child of children) {
640
+ if (child.getProperty("_n") === targetName) {
641
+ return this.getVertexByPathArray(child, path.slice(1));
642
+ }
643
+ }
644
+ return void 0;
645
+ }
646
+ printTree() {
647
+ return this.state.printTree(this.rootVertexId);
648
+ }
649
+ merge(ops) {
650
+ this.applyOps(ops);
651
+ }
652
+ compareStructure(other) {
653
+ return _RepTree.compareVertices(this.rootVertexId, this, other);
654
+ }
655
+ compareMoveOps(other) {
656
+ const movesA = this.moveOps;
657
+ const movesB = other.getMoveOps();
658
+ if (movesA.length !== movesB.length) {
659
+ return false;
660
+ }
661
+ for (let i = 0; i < movesA.length; i++) {
662
+ if (!OpId.equals(movesA[i].id, movesB[i].id)) {
663
+ return false;
664
+ }
665
+ }
666
+ return true;
667
+ }
668
+ /** Checks if the given `ancestorId` is an ancestor of `childId` in the tree */
669
+ isAncestor(childId, ancestorId) {
670
+ let targetId = childId;
671
+ let vertex;
672
+ let depth = 0;
673
+ while (vertex = this.state.getVertex(targetId)) {
674
+ if (vertex.parentId === ancestorId) return true;
675
+ if (!vertex.parentId) return false;
676
+ if (depth > this.maxDepth) {
677
+ console.error(`isAncestor: max depth of ${this.maxDepth} reached. Perhaps, we have an infinite loop here.`);
678
+ return true;
679
+ }
680
+ targetId = vertex.parentId;
681
+ depth++;
682
+ }
683
+ return false;
684
+ }
685
+ observeVertex(vertexId, callback) {
686
+ const vertex = this.getVertex(vertexId);
687
+ if (vertex) {
688
+ callback(vertex);
689
+ }
690
+ const unsubscribe = this.observe(vertexId, (_) => {
691
+ const vertex2 = this.getVertex(vertexId);
692
+ if (vertex2) {
693
+ callback(vertex2);
694
+ }
695
+ });
696
+ return () => {
697
+ unsubscribe();
698
+ };
699
+ }
700
+ observeVertexMove(callback) {
701
+ const listener = (events) => {
702
+ const moveEvent = events.find((e) => e.type === "move");
703
+ if (moveEvent) {
704
+ const vertex = this.getVertex(moveEvent.vertexId);
705
+ if (vertex) {
706
+ callback(vertex, moveEvent.oldParentId === void 0);
707
+ }
708
+ }
709
+ };
710
+ this.state.addGlobalChangeCallback(listener);
711
+ return () => this.state.removeGlobalChangeCallback(listener);
712
+ }
713
+ observe(vertexId, callback) {
714
+ this.state.addChangeCallback(vertexId, callback);
715
+ return () => this.state.removeChangeCallback(vertexId, callback);
716
+ }
717
+ observeOpApplied(callback) {
718
+ this.opAppliedCallbacks.push(callback);
719
+ return () => this.opAppliedCallbacks = this.opAppliedCallbacks.filter((l) => l !== callback);
720
+ }
721
+ static compareVertices(vertexId, treeA, treeB) {
722
+ const childrenA = treeA.state.getChildrenIds(vertexId);
723
+ const childrenB = treeB.state.getChildrenIds(vertexId);
724
+ if (childrenA.length !== childrenB.length) {
725
+ return false;
726
+ }
727
+ if (vertexId !== null) {
728
+ const propertiesA = treeA.getVertexProperties(vertexId);
729
+ const propertiesB = treeB.getVertexProperties(vertexId);
730
+ if (propertiesA.length !== propertiesB.length) {
731
+ return false;
732
+ }
733
+ for (const propA of propertiesA) {
734
+ const propB = propertiesB.find((p) => p.key === propA.key);
735
+ if (!propB || propA.value !== propB.value) {
736
+ return false;
737
+ }
738
+ }
739
+ }
740
+ for (const childId of childrenA) {
741
+ if (!childrenB.includes(childId)) {
742
+ return false;
743
+ }
744
+ if (!_RepTree.compareVertices(childId, treeA, treeB)) {
745
+ return false;
746
+ }
747
+ }
748
+ return true;
749
+ }
750
+ newVertexInternal(vertexId, parentId) {
751
+ this.lamportClock++;
752
+ const op = newMoveVertexOp(this.lamportClock, this.peerId, vertexId, parentId);
753
+ this.localOps.push(op);
754
+ this.applyMove(op);
755
+ this.setVertexProperty(vertexId, "_c", (/* @__PURE__ */ new Date()).toISOString());
756
+ return vertexId;
757
+ }
758
+ newVertexInternalWithUUID(parentId) {
759
+ const vertexId = uuid();
760
+ return this.newVertexInternal(vertexId, parentId);
761
+ }
762
+ ensureTrashVertex() {
763
+ const vertexId = _RepTree.TRASH_VERTEX_ID;
764
+ if (this.state.getVertex(vertexId)) {
765
+ return;
766
+ }
767
+ this.newVertexInternal(vertexId, null);
768
+ }
769
+ /** Updates the lamport clock with the counter value of the operation */
770
+ updateLamportClock(operation) {
771
+ if (operation.id.counter > this.lamportClock) {
772
+ this.lamportClock = operation.id.counter;
773
+ }
774
+ }
775
+ applyMove(op) {
776
+ if (op.parentId !== null && !this.state.getVertex(op.parentId)) {
777
+ if (!this.pendingMovesWithMissingParent.has(op.parentId)) {
778
+ this.pendingMovesWithMissingParent.set(op.parentId, []);
779
+ }
780
+ this.pendingMovesWithMissingParent.get(op.parentId).push(op);
781
+ return;
782
+ }
783
+ this.updateLamportClock(op);
784
+ const lastOp = this.moveOps.length > 0 ? this.moveOps[this.moveOps.length - 1] : null;
785
+ if (lastOp === null || op.id.isGreaterThan(lastOp.id)) {
786
+ this.moveOps.push(op);
787
+ this.reportOpAsApplied(op);
788
+ this.tryToMove(op);
789
+ } else {
790
+ let targetIndex = this.moveOps.length;
791
+ for (let i = this.moveOps.length - 1; i >= 0; i--) {
792
+ const moveOp = this.moveOps[i];
793
+ targetIndex = i;
794
+ if (op.id.isGreaterThan(moveOp.id)) {
795
+ break;
796
+ } else {
797
+ this.undoMove(moveOp);
798
+ }
799
+ }
800
+ this.moveOps.splice(targetIndex + 1, 0, op);
801
+ this.reportOpAsApplied(op);
802
+ this.tryToMove(op);
803
+ for (let i = targetIndex + 2; i < this.moveOps.length; i++) {
804
+ this.tryToMove(this.moveOps[i]);
805
+ }
806
+ }
807
+ this.applyPendingMovesForParent(op.targetId);
808
+ }
809
+ reportOpAsApplied(op) {
810
+ this.appliedOps.add(op.id.toString());
811
+ for (const callback of this.opAppliedCallbacks) {
812
+ callback(op);
813
+ }
814
+ }
815
+ applyOps(ops) {
816
+ for (const op of ops) {
817
+ if (this.appliedOps.has(op.id.toString())) {
818
+ continue;
819
+ }
820
+ if (isMoveVertexOp(op)) {
821
+ this.applyMove(op);
822
+ } else if (isSetPropertyOp(op)) {
823
+ this.applyProperty(op);
824
+ }
825
+ }
826
+ }
827
+ /** Applies operations in an optimized way, sorting move ops by OpId to avoid undo-do-redo cycles */
828
+ applyOpsOptimizedForLotsOfMoves(ops) {
829
+ const newMoveOps = ops.filter((op) => isMoveVertexOp(op) && !this.appliedOps.has(op.id.toString()));
830
+ if (newMoveOps.length > 0) {
831
+ const allMoveOps = [...this.moveOps, ...newMoveOps];
832
+ allMoveOps.sort((a, b) => OpId.compare(a.id, b.id));
833
+ for (let i = 0, len = allMoveOps.length; i < len; i++) {
834
+ const op = allMoveOps[i];
835
+ this.applyMove(op);
836
+ }
837
+ }
838
+ const propertyOps = ops.filter((op) => isSetPropertyOp(op) && !this.appliedOps.has(op.id.toString()));
839
+ for (let i = 0, len = propertyOps.length; i < len; i++) {
840
+ const op = propertyOps[i];
841
+ this.applyProperty(op);
842
+ }
843
+ }
844
+ applyPendingMovesForParent(parentId) {
845
+ if (!this.state.getVertex(parentId)) {
846
+ return;
847
+ }
848
+ const pendingMoves = this.pendingMovesWithMissingParent.get(parentId);
849
+ if (!pendingMoves) {
850
+ return;
851
+ }
852
+ this.pendingMovesWithMissingParent.delete(parentId);
853
+ for (const pendingOp of pendingMoves) {
854
+ this.applyMove(pendingOp);
855
+ }
856
+ }
857
+ tryToMove(op) {
858
+ let targetVertex = this.state.getVertex(op.targetId);
859
+ if (targetVertex) {
860
+ this.parentIdBeforeMove.set(op.id, targetVertex.parentId);
861
+ }
862
+ if (op.targetId === op.parentId) return;
863
+ if (op.parentId && this.isAncestor(op.parentId, op.targetId)) return;
864
+ this.state.moveVertex(op.targetId, op.parentId);
865
+ if (!targetVertex) {
866
+ const pendingProperties = this.pendingPropertiesWithMissingVertex.get(op.targetId) || [];
867
+ for (const prop of pendingProperties) {
868
+ this.setPropertyAndItsOpId(prop);
869
+ }
870
+ }
871
+ }
872
+ undoMove(op) {
873
+ const targetVertex = this.state.getVertex(op.targetId);
874
+ if (!targetVertex) {
875
+ console.error(`An attempt to undo move operation ${op.id.toString()} failed because the target vertex ${op.targetId} not found`);
876
+ return;
877
+ }
878
+ const prevParentId = this.parentIdBeforeMove.get(op.id);
879
+ if (prevParentId === void 0) {
880
+ return;
881
+ }
882
+ this.state.moveVertex(op.targetId, prevParentId);
883
+ }
884
+ setPropertyAndItsOpId(op) {
885
+ this.propertiesAndTheirOpIds.set(`${op.key}@${op.targetId}`, op.id);
886
+ this.state.setProperty(op.targetId, op.key, op.value);
887
+ this.reportOpAsApplied(op);
888
+ }
889
+ setTransientPropertyAndItsOpId(op) {
890
+ this.transientPropertiesAndTheirOpIds.set(`${op.key}@${op.targetId}`, op.id);
891
+ this.state.setTransientProperty(op.targetId, op.key, op.value);
892
+ this.reportOpAsApplied(op);
893
+ }
894
+ applyProperty(op) {
895
+ const targetVertex = this.state.getVertex(op.targetId);
896
+ if (!targetVertex) {
897
+ if (op.transient) {
898
+ return;
899
+ }
900
+ if (!this.pendingPropertiesWithMissingVertex.has(op.targetId)) {
901
+ this.pendingPropertiesWithMissingVertex.set(op.targetId, []);
902
+ }
903
+ this.pendingPropertiesWithMissingVertex.get(op.targetId).push(op);
904
+ return;
905
+ }
906
+ this.updateLamportClock(op);
907
+ const prevTransientOpId = this.transientPropertiesAndTheirOpIds.get(`${op.key}@${op.targetId}`);
908
+ const prevProp = targetVertex.getProperty(op.key);
909
+ const prevOpId = this.propertiesAndTheirOpIds.get(`${op.key}@${op.targetId}`);
910
+ if (!op.transient) {
911
+ this.setPropertyOps.push(op);
912
+ if (!prevProp || !prevOpId || op.id.isGreaterThan(prevOpId)) {
913
+ this.setPropertyAndItsOpId(op);
914
+ }
915
+ if (prevTransientOpId && op.id.isGreaterThan(prevTransientOpId)) {
916
+ this.transientPropertiesAndTheirOpIds.delete(`${op.key}@${op.targetId}`);
917
+ targetVertex.removeTransientProperty(op.key);
918
+ }
919
+ } else {
920
+ if (!prevTransientOpId || op.id.isGreaterThan(prevTransientOpId)) {
921
+ this.setTransientPropertyAndItsOpId(op);
922
+ }
923
+ }
924
+ }
925
+ };
926
+ _RepTree.TRASH_VERTEX_ID = "t";
927
+ _RepTree.DEFAULT_MAX_DEPTH = 1e5;
928
+ var RepTree = _RepTree;
929
+ export {
930
+ OpId,
931
+ RepTree,
932
+ TreeState,
933
+ Vertex,
934
+ VertexState,
935
+ isMoveVertexOp,
936
+ isSetPropertyOp,
937
+ newMoveVertexOp,
938
+ newSetTransientVertexPropertyOp,
939
+ newSetVertexPropertyOp,
940
+ uuid
941
+ };
942
+ //# sourceMappingURL=index.js.map