reptree 0.4.0 → 0.6.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 CHANGED
@@ -1,98 +1,97 @@
1
- # RepTree
1
+ # RepTree - replicated trees with properties
2
2
 
3
- A tree data structure using CRDTs for seamless replication between peers.
3
+ A JavaScript tree data structure for storing and syncing app state. It can be used both to represent and persist the state in the frontend and backend.
4
4
 
5
- > 🚧 **Work in Progress**: This package is under active development and APIs may change.
6
- >
7
- > RepTree was created for the [Supa](https://github.com/supaorg/supa) project, an open-source alternative to ChatGPT.
5
+ RepTree uses [CRDTs](https://crdt.tech/) for seamless replication between users.
8
6
 
9
- ## Description
7
+ > RepTree was created for the [Sila](https://github.com/silaorg/sila) project, an open-source alternative to ChatGPT.
10
8
 
11
- RepTree uses multiple conflict-free replicated data types (CRDTs) to manage seamless replication between peers:
12
- - A move tree CRDT is used for the tree structure (https://martin.kleppmann.com/papers/move-op.pdf).
13
- - A last writer wins (LWW) CRDT is used for properties.
14
- - Yjs integration for collaborative editing with various shared data types (Text, Array, Map, XML).
9
+ ## What it solves
15
10
 
16
- RepTree can also be viewed as a hierarchical, distributed database. For more details on its database capabilities, see [RepTree as a Database](docs/database.md).
11
+ If you have a tree structure in your app where each vertex/node/leaf can be moved independently by multiple users, you need a solution that resolves conflicts when the same vertex is moved in different ways. Otherwise your tree can diverge or form loops. This includes folder structures (people creating and moving folders), 2D/3D scenes with objects being moved and parented, and Notion‑like documents where blocks with text and other properties are edited by users.
17
12
 
18
- ## Installation
13
+ You probably also want properties on each vertex/node/leaf and to have them sync correctly between peers without conflicts. RepTree syncs properties too.
14
+
15
+ ## Getting started
19
16
 
20
17
  ```bash
21
18
  npm install reptree
22
19
  ```
23
20
 
24
- ## Usage
25
-
26
- ### Reactive vertex with Zod (optional)
27
-
21
+ ### Example 1
28
22
  ```ts
29
- import { RepTree } from 'reptree';
30
- import { z } from 'zod';
23
+ import { RepTree } from "reptree";
31
24
 
32
- const tree = new RepTree('peer1');
33
- const root = tree.createRoot();
34
- const v = root.newChild();
35
-
36
- const Person = z.object({ name: z.string(), age: z.number().int().min(0) });
25
+ // Create a tree with a root
26
+ const tree = new RepTree("company-org-1");
27
+ const company = tree.createRoot();
37
28
 
38
- const person = v.bind(Person);
29
+ // Create a node (we call them vertices in RepTree) in the root of our new tree
30
+ const devs = company.newNamedChild("developers");
31
+ const qa = company.newNamedChild("qa");
39
32
 
40
- person.name = 'Alice'; // validated and persisted
41
- person.age = 33; // validated and persisted
42
- ```
33
+ // Create a vertex in another vertex
34
+ const alice = qa.newChild();
43
35
 
44
- #### Aliases for internal fields
36
+ // Set properties
37
+ alice.setProperty("name", "Alice");
38
+ alice.setProperty("age", 32);
45
39
 
46
- - `name` `_n`
47
- - `createdAt` ↔ `_c` (Date exposed, ISO stored)
40
+ // Move the vertex inside a different vertex
41
+ alice.moveTo(devs);
48
42
 
49
- These aliases are applied by default when using `vertex.bind()`.
43
+ // Bind a vertex to a type to set its properties like regular fields
44
+ const bob = qa.newChild().bind<{ name: string; age: number }>();
45
+ bob.name = "Bob";
46
+ bob.age = 33;
50
47
 
51
- ```ts
52
- person.name = 'Alice'; // writes _n
53
- person.createdAt = new Date(); // writes _c (ISO)
54
- console.log(person.createdAt instanceof Date); // true
48
+ // Use a Zod type for runtime type checks
49
+ import { z } from "zod";
50
+ const Person = z.object({ name: z.string(), age: z.number().int().min(0) });
51
+ const casey = devs.newNamedChild("Casey").bind(Person);
52
+ casey.name = "Casey";
53
+ casey.age = 34;
55
54
  ```
56
55
 
57
- For more, see `docs/reactive-vertices.md`.
56
+ ### Example 2
58
57
 
59
58
  ```typescript
60
- import { RepTree } from 'reptree';
59
+ import { RepTree } from "reptree";
61
60
 
62
61
  // Create a new tree
63
- const tree = new RepTree('peer1');
62
+ const tree = new RepTree("peer1");
64
63
  const root = tree.createRoot();
65
- root.name = 'Project';
64
+ root.name = "Project";
66
65
 
67
66
  // Create a folder structure with properties
68
- const docsFolder = root.newNamedChild('Docs');
67
+ const docsFolder = root.newNamedChild("Docs");
69
68
  docsFolder.setProperties({
70
- type: 'folder',
71
- icon: 'folder-icon'
69
+ type: "folder",
70
+ icon: "folder-icon",
72
71
  });
73
72
 
74
- const imagesFolder = root.newNamedChild('Images');
73
+ const imagesFolder = root.newNamedChild("Images");
75
74
  imagesFolder.setProperties({
76
- type: 'folder',
77
- icon: 'image-icon'
75
+ type: "folder",
76
+ icon: "image-icon",
78
77
  });
79
78
 
80
79
  // Add files to folders
81
- const readmeFile = docsFolder.newNamedChild('README.md');
80
+ const readmeFile = docsFolder.newNamedChild("README.md");
82
81
  readmeFile.setProperties({
83
- type: 'file',
82
+ type: "file",
84
83
  size: 2048,
85
- lastModified: '2023-10-15T14:22:10Z',
86
- s3Path: 's3://my-bucket/docs/README.md'
84
+ lastModified: "2023-10-15T14:22:10Z",
85
+ s3Path: "s3://my-bucket/docs/README.md",
87
86
  });
88
87
 
89
- const logoFile = imagesFolder.newNamedChild('logo.png');
88
+ const logoFile = imagesFolder.newNamedChild("logo.png");
90
89
  logoFile.setProperties({
91
- type: 'file',
90
+ type: "file",
92
91
  size: 15360,
93
- dimensions: '512x512',
94
- format: 'png',
95
- s3Path: 's3://my-bucket/images/logo.png'
92
+ dimensions: "512x512",
93
+ format: "png",
94
+ s3Path: "s3://my-bucket/images/logo.png",
96
95
  });
97
96
 
98
97
  // Move a file to a different folder
@@ -102,65 +101,17 @@ logoFile.moveTo(docsFolder);
102
101
  const docsFolderContents = docsFolder.children;
103
102
 
104
103
  // Syncing between trees
105
- const otherTree = new RepTree('peer2');
104
+ const otherTree = new RepTree("peer2");
106
105
  const ops = tree.getAllOps();
107
106
  otherTree.merge(ops);
108
107
  ```
109
108
 
110
- ### Creating children with normalized props
111
-
112
- `vertex.newChild(props)` and `vertex.newNamedChild(name, props)` accept plain objects. RepTree will:
113
-
114
- - Map `name` → `_n`, `createdAt` (Date) → `_c` (ISO)
115
- - Filter unsupported types (non-primitive objects except Y.Doc)
116
- - Ignore `props.name` if `newNamedChild` has an explicit `name`
117
- - Forbid nested children in props for now
118
-
119
- ## Yjs Integration
120
-
121
- RepTree supports [Yjs](https://github.com/yjs/yjs) documents as vertex properties, enabling real-time collaborative editing with a variety of shared data types:
122
-
123
- ```typescript
124
- import { RepTree } from 'reptree';
125
- import * as Y from 'yjs';
126
-
127
- // Create a tree with a root vertex
128
- const tree = new RepTree('peer1');
129
- const root = tree.createRoot();
130
-
131
- // Create a Yjs document
132
- const ydoc = new Y.Doc();
133
- const ytext = ydoc.getText('default');
134
- ytext.insert(0, 'Hello world');
135
-
136
- // Set the Yjs document as a property
137
- root.setProperty('content', ydoc);
138
-
139
- // Later, retrieve and modify the document
140
- const retrievedDoc = root.getProperty('content') as Y.Doc;
141
- retrievedDoc.getText('default').insert(retrievedDoc.getText('default').length, '!');
142
-
143
- // Sync operations with another tree
144
- const tree2 = new RepTree('peer2');
145
- tree2.merge(tree.popLocalOps());
146
-
147
- // Both trees now have the same Yjs document content
148
- const root2 = tree2.root;
149
- const doc2 = root2.getProperty('content') as Y.Doc;
150
- console.log(doc2.getText('default').toString()); // 'Hello world!'
151
- ```
109
+ ## CRDTs
152
110
 
153
- This integration allows for:
154
- - Collaborative editing with multiple shared data types:
155
- - **Y.Text** - For rich text editing with formatting attributes
156
- - **Y.Array** - For ordered collections of data
157
- - **Y.Map** - For key-value pairs and structured data
158
- - **Y.XmlFragment/Y.XmlElement** - For XML-like structured content
159
- - Complex nested data structures (arrays within maps, maps within arrays, etc.)
160
- - Automatic CRDT synchronization between peers
161
- - Conflict-free concurrent editing
162
- - Integration with existing Yjs ecosystem (editors, frameworks, etc.)
111
+ RepTree uses two conflict-free replicated data types (CRDTs):
112
+ - A move tree CRDT for the tree structure (https://martin.kleppmann.com/papers/move-op.pdf).
113
+ - A last-writer-wins (LWW) CRDT is for properties.
163
114
 
164
115
  ## License
165
116
 
166
- MIT
117
+ MIT
package/dist/index.cjs CHANGED
@@ -1,9 +1,7 @@
1
1
  "use strict";
2
- var __create = Object.create;
3
2
  var __defProp = Object.defineProperty;
4
3
  var __getOwnPropDesc = Object.getOwnPropertyDescriptor;
5
4
  var __getOwnPropNames = Object.getOwnPropertyNames;
6
- var __getProtoOf = Object.getPrototypeOf;
7
5
  var __hasOwnProp = Object.prototype.hasOwnProperty;
8
6
  var __export = (target, all) => {
9
7
  for (var name in all)
@@ -17,14 +15,6 @@ var __copyProps = (to, from, except, desc) => {
17
15
  }
18
16
  return to;
19
17
  };
20
- var __toESM = (mod, isNodeMode, target) => (target = mod != null ? __create(__getProtoOf(mod)) : {}, __copyProps(
21
- // If the importer is in node compatibility mode or this is not an ESM
22
- // file that has been converted to a CommonJS file using a Babel-
23
- // compatible transform (i.e. "__esModule" has not been set), then set
24
- // "default" to the CommonJS "module.exports" for node compatibility.
25
- isNodeMode || !mod || !mod.__esModule ? __defProp(target, "default", { value: mod, enumerable: true }) : target,
26
- mod
27
- ));
28
18
  var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: true }), mod);
29
19
 
30
20
  // src/index.ts
@@ -36,10 +26,7 @@ __export(index_exports, {
36
26
  Vertex: () => Vertex,
37
27
  VertexState: () => VertexState,
38
28
  bindVertex: () => bindVertex,
39
- defaultAliases: () => defaultAliases,
40
29
  isAnyPropertyOp: () => isAnyPropertyOp,
41
- isLWWPropertyOp: () => isLWWPropertyOp,
42
- isModifyPropertyOp: () => isModifyPropertyOp,
43
30
  isMoveVertexOp: () => isMoveVertexOp,
44
31
  newMoveVertexOp: () => newMoveVertexOp,
45
32
  newSetTransientVertexPropertyOp: () => newSetTransientVertexPropertyOp,
@@ -102,12 +89,6 @@ function isMoveVertexOp(op) {
102
89
  function isAnyPropertyOp(op) {
103
90
  return "key" in op;
104
91
  }
105
- function isLWWPropertyOp(op) {
106
- return "key" in op && "value" in op && (!op.value || typeof op.value !== "object" || !("type" in op.value));
107
- }
108
- function isModifyPropertyOp(op) {
109
- return "key" in op && "value" in op && typeof op.value === "object" && op.value !== null && "type" in op.value;
110
- }
111
92
  function newMoveVertexOp(clock, peerId, targetId, parentId) {
112
93
  return { id: createOpId(clock, peerId), targetId, parentId };
113
94
  }
@@ -373,7 +354,7 @@ var TreeState = class {
373
354
  const vertex = this.getVertex(vertexId);
374
355
  if (vertex) {
375
356
  for (const prop of vertex.getAllProperties()) {
376
- if (prop.key === "_n") {
357
+ if (prop.key === "name") {
377
358
  vertexName = prop.value;
378
359
  }
379
360
  const propPrefix = indent + (isLast ? " " : "\u2502 ") + "\u2022 ";
@@ -386,8 +367,8 @@ var TreeState = class {
386
367
  const sortedChildren = [...children].sort((a, b) => {
387
368
  const vertexA = this.getVertex(a);
388
369
  const vertexB = this.getVertex(b);
389
- const nameA = vertexA?.getProperty("_n");
390
- const nameB = vertexB?.getProperty("_n");
370
+ const nameA = vertexA?.getProperty("name");
371
+ const nameB = vertexB?.getProperty("name");
391
372
  if (nameA && nameB) {
392
373
  return nameA.localeCompare(nameB);
393
374
  }
@@ -411,30 +392,10 @@ function uuid() {
411
392
  }
412
393
 
413
394
  // src/reactive.ts
414
- var defaultAliases = [
415
- { publicKey: "name", internalKey: "_n" },
416
- {
417
- publicKey: "createdAt",
418
- internalKey: "_c",
419
- toPublic: (v) => typeof v === "string" ? new Date(v) : v,
420
- toInternal: (v) => v instanceof Date ? v.toISOString() : v
421
- }
422
- ];
423
- function buildAliasMaps(aliases) {
424
- const publicToInternal = /* @__PURE__ */ new Map();
425
- const internalToPublic = /* @__PURE__ */ new Map();
426
- for (const rule of aliases) {
427
- publicToInternal.set(rule.publicKey, rule);
428
- internalToPublic.set(rule.internalKey, rule);
429
- }
430
- return { publicToInternal, internalToPublic };
431
- }
432
395
  function bindVertex(tree, id, schemaOrOptions) {
433
- const isOptions = typeof schemaOrOptions === "object" && schemaOrOptions !== null && (Object.prototype.hasOwnProperty.call(schemaOrOptions, "aliases") || Object.prototype.hasOwnProperty.call(schemaOrOptions, "includeInternalKeys") || Object.prototype.hasOwnProperty.call(schemaOrOptions, "schema"));
396
+ const isOptions = typeof schemaOrOptions === "object" && schemaOrOptions !== null && (Object.prototype.hasOwnProperty.call(schemaOrOptions, "includeInternalKeys") || Object.prototype.hasOwnProperty.call(schemaOrOptions, "schema"));
434
397
  const options = isOptions ? schemaOrOptions : { schema: schemaOrOptions };
435
398
  const schema = options.schema;
436
- const aliases = options.aliases ?? defaultAliases;
437
- const { publicToInternal } = buildAliasMaps(aliases);
438
399
  const obj = {};
439
400
  Object.defineProperties(obj, {
440
401
  $vertex: { get: () => tree.getVertex(id), enumerable: false, configurable: true },
@@ -471,19 +432,14 @@ function bindVertex(tree, id, schemaOrOptions) {
471
432
  const transientProxy = new Proxy({}, {
472
433
  set(_, prop, value) {
473
434
  if (typeof prop === "string") {
474
- const rule = publicToInternal.get(prop);
475
- const internalKey = rule?.internalKey ?? prop;
476
- const internalValue = rule?.toInternal ? rule.toInternal(value) : value;
477
- tree.setTransientVertexProperty(id, internalKey, internalValue);
435
+ tree.setTransientVertexProperty(id, prop, value);
478
436
  }
479
437
  return true;
480
438
  },
481
439
  get(_, prop) {
482
440
  if (typeof prop !== "string") return void 0;
483
- const rule = publicToInternal.get(prop);
484
- const internalKey = rule?.internalKey ?? prop;
485
- const rawValue = tree.getVertexProperty(id, internalKey, true);
486
- return rule?.toPublic ? rule.toPublic(rawValue) : rawValue;
441
+ const rawValue = tree.getVertexProperty(id, prop, true);
442
+ return rawValue;
487
443
  }
488
444
  });
489
445
  fn(transientProxy);
@@ -513,10 +469,8 @@ function bindVertex(tree, id, schemaOrOptions) {
513
469
  if (prop in target) {
514
470
  return Reflect.get(target, prop, receiver);
515
471
  }
516
- const rule = publicToInternal.get(prop);
517
- const internalKey = rule?.internalKey ?? prop;
518
- const rawValue = tree.getVertexProperty(id, internalKey, true);
519
- return rule?.toPublic ? rule.toPublic(rawValue) : rawValue;
472
+ const rawValue = tree.getVertexProperty(id, prop, true);
473
+ return rawValue;
520
474
  },
521
475
  set(target, prop, value) {
522
476
  if (typeof prop !== "string") {
@@ -530,19 +484,14 @@ function bindVertex(tree, id, schemaOrOptions) {
530
484
  value = res.data;
531
485
  }
532
486
  }
533
- const rule = publicToInternal.get(prop);
534
- const internalKey = rule?.internalKey ?? prop;
535
- const internalValue = rule?.toInternal ? rule.toInternal(value) : value;
536
- tree.setVertexProperty(id, internalKey, internalValue);
487
+ tree.setVertexProperty(id, prop, value);
537
488
  return true;
538
489
  },
539
490
  deleteProperty(_target, prop) {
540
491
  if (typeof prop !== "string") {
541
492
  return true;
542
493
  }
543
- const rule = publicToInternal.get(prop);
544
- const internalKey = rule?.internalKey ?? prop;
545
- tree.setVertexProperty(id, internalKey, void 0);
494
+ tree.setVertexProperty(id, prop, void 0);
546
495
  return true;
547
496
  }
548
497
  });
@@ -550,7 +499,6 @@ function bindVertex(tree, id, schemaOrOptions) {
550
499
  }
551
500
 
552
501
  // src/Vertex.ts
553
- var Y = __toESM(require("yjs"), 1);
554
502
  var Vertex = class _Vertex {
555
503
  constructor(tree, state) {
556
504
  this.tree = tree;
@@ -560,10 +508,10 @@ var Vertex = class _Vertex {
560
508
  return this.state.id;
561
509
  }
562
510
  get name() {
563
- return this.getProperty("_n");
511
+ return this.getProperty("name");
564
512
  }
565
513
  set name(name) {
566
- this.tree.setVertexProperty(this.id, "_n", name);
514
+ this.tree.setVertexProperty(this.id, "name", name);
567
515
  }
568
516
  get createdAt() {
569
517
  const createdAt = this.getProperty("_c");
@@ -683,7 +631,6 @@ var Vertex = class _Vertex {
683
631
  }
684
632
  /**
685
633
  * Normalizes an input props object for vertex creation:
686
- * - Aliases name -> _n, createdAt -> _c (Date -> ISO string)
687
634
  * - Filters unsupported field types with a console warning
688
635
  * - When a name param is provided to newNamedChild, ignores conflicting name in props
689
636
  */
@@ -698,14 +645,9 @@ var Vertex = class _Vertex {
698
645
  }
699
646
  if (rawKey === "children") continue;
700
647
  let key = rawKey;
701
- if (rawKey === "name") {
702
- if (explicitName !== void 0) {
703
- console.warn('newNamedChild: "name" in props is ignored because a name argument was provided');
704
- continue;
705
- }
706
- key = "_n";
707
- } else if (rawKey === "createdAt") {
708
- key = "_c";
648
+ if (rawKey === "name" && explicitName !== void 0) {
649
+ console.warn('newNamedChild: "name" in props is ignored because a name argument was provided');
650
+ continue;
709
651
  }
710
652
  let value = rawValue;
711
653
  if (key === "_c") {
@@ -724,10 +666,8 @@ var Vertex = class _Vertex {
724
666
  continue;
725
667
  }
726
668
  } else if (typeof value === "object" && value !== null) {
727
- if (!(value instanceof Y.Doc)) {
728
- skipped.push(rawKey);
729
- continue;
730
- }
669
+ skipped.push(rawKey);
670
+ continue;
731
671
  } else if (!isPrimitive(value)) {
732
672
  skipped.push(rawKey);
733
673
  continue;
@@ -927,7 +867,6 @@ var StateVector = class _StateVector {
927
867
  };
928
868
 
929
869
  // src/RepTree.ts
930
- var Y2 = __toESM(require("yjs"), 1);
931
870
  var _RepTree = class _RepTree {
932
871
  /**
933
872
  * @param peerId - The peer ID of the current client. Should be unique across all peers.
@@ -939,7 +878,7 @@ var _RepTree = class _RepTree {
939
878
  this.setPropertyOps = [];
940
879
  this.propertiesAndTheirOpIds = /* @__PURE__ */ new Map();
941
880
  this.transientPropertiesAndTheirOpIds = /* @__PURE__ */ new Map();
942
- this.yjsObservers = /* @__PURE__ */ new Map();
881
+ // Observers for non-structural properties are not used
943
882
  this.localOps = [];
944
883
  this.pendingMovesWithMissingParent = /* @__PURE__ */ new Map();
945
884
  this.pendingPropertiesWithMissingVertex = /* @__PURE__ */ new Map();
@@ -1041,6 +980,12 @@ var _RepTree = class _RepTree {
1041
980
  this.localOps = [];
1042
981
  return ops;
1043
982
  }
983
+ /**
984
+ * This is the first vertex that will contain all other vertices.
985
+ * If you plan to replicate a tree then don't use this method and instead merge
986
+ * in the ops from another tree (that will also contain the root vertex).
987
+ * @returns The root vertex
988
+ */
1044
989
  createRoot() {
1045
990
  if (this.rootVertexId) {
1046
991
  throw new Error("Root vertex already exists");
@@ -1070,7 +1015,7 @@ var _RepTree = class _RepTree {
1070
1015
  if (typedProps) {
1071
1016
  this.setVertexProperties(vertexId, typedProps);
1072
1017
  }
1073
- this.setVertexProperty(vertexId, "_n", name);
1018
+ this.setVertexProperty(vertexId, "name", name);
1074
1019
  const vertex = this.state.getVertex(vertexId);
1075
1020
  if (!vertex) {
1076
1021
  throw new Error("Failed to create named vertex");
@@ -1088,21 +1033,15 @@ var _RepTree = class _RepTree {
1088
1033
  }
1089
1034
  setTransientVertexProperty(vertexId, key, value) {
1090
1035
  this.lamportClock++;
1091
- let opValue;
1092
- if (value instanceof Y2.Doc) {
1093
- const state = Y2.encodeStateAsUpdate(value);
1094
- opValue = {
1095
- type: "yjs",
1096
- value: state
1097
- };
1098
- this.setupYjsObserver(value, vertexId, key);
1099
- } else {
1100
- opValue = value;
1101
- }
1102
- const op = newSetTransientVertexPropertyOp(this.lamportClock, this.peerId, vertexId, key, opValue);
1036
+ const op = newSetTransientVertexPropertyOp(this.lamportClock, this.peerId, vertexId, key, value);
1103
1037
  this.localOps.push(op);
1104
1038
  this.applyProperty(op);
1105
1039
  }
1040
+ /**
1041
+ * Promotes all transient (temporary) properties to persistent properties.
1042
+ * @param vertexId - The ID of the vertex to commit transients for.
1043
+ * @returns
1044
+ */
1106
1045
  commitTransients(vertexId) {
1107
1046
  const vertex = this.state.getVertex(vertexId);
1108
1047
  if (!vertex) {
@@ -1119,18 +1058,7 @@ var _RepTree = class _RepTree {
1119
1058
  }
1120
1059
  setVertexProperty(vertexId, key, value) {
1121
1060
  this.lamportClock++;
1122
- let opValue;
1123
- if (value instanceof Y2.Doc) {
1124
- const state = Y2.encodeStateAsUpdate(value);
1125
- opValue = {
1126
- type: "yjs",
1127
- value: state
1128
- };
1129
- this.setupYjsObserver(value, vertexId, key);
1130
- } else {
1131
- opValue = value;
1132
- }
1133
- const op = newSetVertexPropertyOp(this.lamportClock, this.peerId, vertexId, key, opValue);
1061
+ const op = newSetVertexPropertyOp(this.lamportClock, this.peerId, vertexId, key, value);
1134
1062
  this.localOps.push(op);
1135
1063
  this.applyProperty(op);
1136
1064
  }
@@ -1161,7 +1089,7 @@ var _RepTree = class _RepTree {
1161
1089
  const targetName = path[0];
1162
1090
  const children = this.getChildren(vertex.id);
1163
1091
  for (const child of children) {
1164
- if (child.getProperty("_n") === targetName) {
1092
+ if (child.getProperty("name") === targetName) {
1165
1093
  return this.getVertexByPathArray(child, path.slice(1));
1166
1094
  }
1167
1095
  }
@@ -1293,13 +1221,7 @@ var _RepTree = class _RepTree {
1293
1221
  if (!propB) {
1294
1222
  return false;
1295
1223
  }
1296
- if (propA.value instanceof Y2.Doc && propB.value instanceof Y2.Doc) {
1297
- const snapshotA = Y2.snapshot(propA.value);
1298
- const snapshotB = Y2.snapshot(propB.value);
1299
- if (!Y2.equalSnapshots(snapshotA, snapshotB)) {
1300
- return false;
1301
- }
1302
- } else if (propA.value !== propB.value) {
1224
+ if (propA.value !== propB.value) {
1303
1225
  return false;
1304
1226
  }
1305
1227
  }
@@ -1396,40 +1318,6 @@ var _RepTree = class _RepTree {
1396
1318
  this.state.setTransientProperty(op.targetId, op.key, op.value);
1397
1319
  this.reportOpAsApplied(op);
1398
1320
  }
1399
- setupYjsObserver(doc, vertexId, key) {
1400
- const propertyKey = `${key}@${vertexId}`;
1401
- if (this.yjsObservers.has(propertyKey)) {
1402
- const existingDoc = this.getVertexProperty(vertexId, key);
1403
- if (existingDoc instanceof Y2.Doc) {
1404
- existingDoc.off("update", this.yjsObservers.get(propertyKey));
1405
- }
1406
- this.yjsObservers.delete(propertyKey);
1407
- }
1408
- const ydocObserver = (update, origin, doc2, transaction) => {
1409
- if (!transaction.local) {
1410
- return;
1411
- }
1412
- const crdtValue = {
1413
- type: "yjs",
1414
- value: update
1415
- };
1416
- this.lamportClock++;
1417
- const op = newSetVertexPropertyOp(
1418
- this.lamportClock,
1419
- this.peerId,
1420
- vertexId,
1421
- key,
1422
- crdtValue
1423
- );
1424
- this.localOps.push(op);
1425
- this.applyProperty(op);
1426
- if (this._stateVectorEnabled) {
1427
- this.stateVector.updateFromOp(op);
1428
- }
1429
- };
1430
- doc.on("update", ydocObserver);
1431
- this.yjsObservers.set(propertyKey, ydocObserver);
1432
- }
1433
1321
  applyProperty(op) {
1434
1322
  const targetVertex = this.state.getVertex(op.targetId);
1435
1323
  if (!targetVertex) {
@@ -1443,11 +1331,7 @@ var _RepTree = class _RepTree {
1443
1331
  return;
1444
1332
  }
1445
1333
  this.updateLamportClock(op);
1446
- if (isModifyPropertyOp(op)) {
1447
- this.applyModifyProperty(op, targetVertex);
1448
- } else {
1449
- this.applyLLWProperty(op, targetVertex);
1450
- }
1334
+ this.applyLLWProperty(op, targetVertex);
1451
1335
  }
1452
1336
  applyLLWProperty(op, targetVertex) {
1453
1337
  const prevTransientOpId = this.transientPropertiesAndTheirOpIds.get(`${op.key}@${op.targetId}`);
@@ -1469,28 +1353,7 @@ var _RepTree = class _RepTree {
1469
1353
  }
1470
1354
  }
1471
1355
  }
1472
- applyModifyProperty(op, targetVertex) {
1473
- if (op.transient) {
1474
- console.warn("Not implemented: transient non LWW property");
1475
- return;
1476
- }
1477
- this.setPropertyOps.push(op);
1478
- const crdtValue = op.value;
1479
- if (crdtValue.type !== "yjs") {
1480
- throw new Error("Unknown CRDT type");
1481
- }
1482
- const ydoc = targetVertex.getProperty(op.key);
1483
- if (ydoc instanceof Y2.Doc) {
1484
- Y2.applyUpdate(ydoc, crdtValue.value);
1485
- } else {
1486
- const newDoc = new Y2.Doc();
1487
- this.setupYjsObserver(newDoc, op.targetId, op.key);
1488
- this.state.setProperty(op.targetId, op.key, newDoc);
1489
- Y2.applyUpdate(newDoc, crdtValue.value);
1490
- }
1491
- this.propertiesAndTheirOpIds.set(`${op.key}@${op.targetId}`, op.id);
1492
- this.reportOpAsApplied(op);
1493
- }
1356
+ // Non-LWW modify-property flow removed
1494
1357
  applyOperation(op) {
1495
1358
  if (isMoveVertexOp(op)) {
1496
1359
  this.applyMove(op);
@@ -1614,10 +1477,7 @@ var RepTree = _RepTree;
1614
1477
  Vertex,
1615
1478
  VertexState,
1616
1479
  bindVertex,
1617
- defaultAliases,
1618
1480
  isAnyPropertyOp,
1619
- isLWWPropertyOp,
1620
- isModifyPropertyOp,
1621
1481
  isMoveVertexOp,
1622
1482
  newMoveVertexOp,
1623
1483
  newSetTransientVertexPropertyOp,