reptree 0.4.0 → 0.5.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 +59 -108
- package/dist/index.cjs +19 -119
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.cts +17 -23
- package/dist/index.d.ts +17 -23
- package/dist/index.js +19 -107
- package/dist/index.js.map +1 -1
- package/package.json +2 -3
package/README.md
CHANGED
|
@@ -1,98 +1,97 @@
|
|
|
1
|
-
# RepTree
|
|
1
|
+
# RepTree - replicated trees with properties
|
|
2
2
|
|
|
3
|
-
A tree data structure
|
|
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
|
-
|
|
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
|
-
|
|
7
|
+
> RepTree was created for the [Sila](https://github.com/silaorg/sila) project, an open-source alternative to ChatGPT.
|
|
10
8
|
|
|
11
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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
|
-
|
|
25
|
-
|
|
26
|
-
### Reactive vertex with Zod (optional)
|
|
27
|
-
|
|
21
|
+
### Example 1
|
|
28
22
|
```ts
|
|
29
|
-
import { RepTree } from
|
|
30
|
-
import { z } from 'zod';
|
|
23
|
+
import { RepTree } from "reptree";
|
|
31
24
|
|
|
32
|
-
|
|
33
|
-
const
|
|
34
|
-
const
|
|
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
|
-
|
|
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
|
-
|
|
41
|
-
|
|
42
|
-
```
|
|
33
|
+
// Create a vertex in another vertex
|
|
34
|
+
const alice = qa.newChild();
|
|
43
35
|
|
|
44
|
-
|
|
36
|
+
// Set properties
|
|
37
|
+
alice.setProperty("name", "Alice");
|
|
38
|
+
alice.setProperty("age", 32);
|
|
45
39
|
|
|
46
|
-
|
|
47
|
-
|
|
40
|
+
// Move the vertex inside a different vertex
|
|
41
|
+
alice.moveTo(devs);
|
|
48
42
|
|
|
49
|
-
|
|
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
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
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
|
-
|
|
56
|
+
### Example 2
|
|
58
57
|
|
|
59
58
|
```typescript
|
|
60
|
-
import { RepTree } from
|
|
59
|
+
import { RepTree } from "reptree";
|
|
61
60
|
|
|
62
61
|
// Create a new tree
|
|
63
|
-
const tree = new RepTree(
|
|
62
|
+
const tree = new RepTree("peer1");
|
|
64
63
|
const root = tree.createRoot();
|
|
65
|
-
root.name =
|
|
64
|
+
root.name = "Project";
|
|
66
65
|
|
|
67
66
|
// Create a folder structure with properties
|
|
68
|
-
const docsFolder = root.newNamedChild(
|
|
67
|
+
const docsFolder = root.newNamedChild("Docs");
|
|
69
68
|
docsFolder.setProperties({
|
|
70
|
-
type:
|
|
71
|
-
icon:
|
|
69
|
+
type: "folder",
|
|
70
|
+
icon: "folder-icon",
|
|
72
71
|
});
|
|
73
72
|
|
|
74
|
-
const imagesFolder = root.newNamedChild(
|
|
73
|
+
const imagesFolder = root.newNamedChild("Images");
|
|
75
74
|
imagesFolder.setProperties({
|
|
76
|
-
type:
|
|
77
|
-
icon:
|
|
75
|
+
type: "folder",
|
|
76
|
+
icon: "image-icon",
|
|
78
77
|
});
|
|
79
78
|
|
|
80
79
|
// Add files to folders
|
|
81
|
-
const readmeFile = docsFolder.newNamedChild(
|
|
80
|
+
const readmeFile = docsFolder.newNamedChild("README.md");
|
|
82
81
|
readmeFile.setProperties({
|
|
83
|
-
type:
|
|
82
|
+
type: "file",
|
|
84
83
|
size: 2048,
|
|
85
|
-
lastModified:
|
|
86
|
-
s3Path:
|
|
84
|
+
lastModified: "2023-10-15T14:22:10Z",
|
|
85
|
+
s3Path: "s3://my-bucket/docs/README.md",
|
|
87
86
|
});
|
|
88
87
|
|
|
89
|
-
const logoFile = imagesFolder.newNamedChild(
|
|
88
|
+
const logoFile = imagesFolder.newNamedChild("logo.png");
|
|
90
89
|
logoFile.setProperties({
|
|
91
|
-
type:
|
|
90
|
+
type: "file",
|
|
92
91
|
size: 15360,
|
|
93
|
-
dimensions:
|
|
94
|
-
format:
|
|
95
|
-
s3Path:
|
|
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(
|
|
104
|
+
const otherTree = new RepTree("peer2");
|
|
106
105
|
const ops = tree.getAllOps();
|
|
107
106
|
otherTree.merge(ops);
|
|
108
107
|
```
|
|
109
108
|
|
|
110
|
-
|
|
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
|
-
|
|
154
|
-
-
|
|
155
|
-
|
|
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
|
|
@@ -38,8 +28,6 @@ __export(index_exports, {
|
|
|
38
28
|
bindVertex: () => bindVertex,
|
|
39
29
|
defaultAliases: () => defaultAliases,
|
|
40
30
|
isAnyPropertyOp: () => isAnyPropertyOp,
|
|
41
|
-
isLWWPropertyOp: () => isLWWPropertyOp,
|
|
42
|
-
isModifyPropertyOp: () => isModifyPropertyOp,
|
|
43
31
|
isMoveVertexOp: () => isMoveVertexOp,
|
|
44
32
|
newMoveVertexOp: () => newMoveVertexOp,
|
|
45
33
|
newSetTransientVertexPropertyOp: () => newSetTransientVertexPropertyOp,
|
|
@@ -102,12 +90,6 @@ function isMoveVertexOp(op) {
|
|
|
102
90
|
function isAnyPropertyOp(op) {
|
|
103
91
|
return "key" in op;
|
|
104
92
|
}
|
|
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
93
|
function newMoveVertexOp(clock, peerId, targetId, parentId) {
|
|
112
94
|
return { id: createOpId(clock, peerId), targetId, parentId };
|
|
113
95
|
}
|
|
@@ -550,7 +532,6 @@ function bindVertex(tree, id, schemaOrOptions) {
|
|
|
550
532
|
}
|
|
551
533
|
|
|
552
534
|
// src/Vertex.ts
|
|
553
|
-
var Y = __toESM(require("yjs"), 1);
|
|
554
535
|
var Vertex = class _Vertex {
|
|
555
536
|
constructor(tree, state) {
|
|
556
537
|
this.tree = tree;
|
|
@@ -724,10 +705,8 @@ var Vertex = class _Vertex {
|
|
|
724
705
|
continue;
|
|
725
706
|
}
|
|
726
707
|
} else if (typeof value === "object" && value !== null) {
|
|
727
|
-
|
|
728
|
-
|
|
729
|
-
continue;
|
|
730
|
-
}
|
|
708
|
+
skipped.push(rawKey);
|
|
709
|
+
continue;
|
|
731
710
|
} else if (!isPrimitive(value)) {
|
|
732
711
|
skipped.push(rawKey);
|
|
733
712
|
continue;
|
|
@@ -927,7 +906,6 @@ var StateVector = class _StateVector {
|
|
|
927
906
|
};
|
|
928
907
|
|
|
929
908
|
// src/RepTree.ts
|
|
930
|
-
var Y2 = __toESM(require("yjs"), 1);
|
|
931
909
|
var _RepTree = class _RepTree {
|
|
932
910
|
/**
|
|
933
911
|
* @param peerId - The peer ID of the current client. Should be unique across all peers.
|
|
@@ -939,7 +917,7 @@ var _RepTree = class _RepTree {
|
|
|
939
917
|
this.setPropertyOps = [];
|
|
940
918
|
this.propertiesAndTheirOpIds = /* @__PURE__ */ new Map();
|
|
941
919
|
this.transientPropertiesAndTheirOpIds = /* @__PURE__ */ new Map();
|
|
942
|
-
|
|
920
|
+
// Observers for non-structural properties are not used
|
|
943
921
|
this.localOps = [];
|
|
944
922
|
this.pendingMovesWithMissingParent = /* @__PURE__ */ new Map();
|
|
945
923
|
this.pendingPropertiesWithMissingVertex = /* @__PURE__ */ new Map();
|
|
@@ -1041,6 +1019,12 @@ var _RepTree = class _RepTree {
|
|
|
1041
1019
|
this.localOps = [];
|
|
1042
1020
|
return ops;
|
|
1043
1021
|
}
|
|
1022
|
+
/**
|
|
1023
|
+
* This is the first vertex that will contain all other vertices.
|
|
1024
|
+
* If you plan to replicate a tree then don't use this method and instead merge
|
|
1025
|
+
* in the ops from another tree (that will also contain the root vertex).
|
|
1026
|
+
* @returns The root vertex
|
|
1027
|
+
*/
|
|
1044
1028
|
createRoot() {
|
|
1045
1029
|
if (this.rootVertexId) {
|
|
1046
1030
|
throw new Error("Root vertex already exists");
|
|
@@ -1088,21 +1072,15 @@ var _RepTree = class _RepTree {
|
|
|
1088
1072
|
}
|
|
1089
1073
|
setTransientVertexProperty(vertexId, key, value) {
|
|
1090
1074
|
this.lamportClock++;
|
|
1091
|
-
|
|
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);
|
|
1075
|
+
const op = newSetTransientVertexPropertyOp(this.lamportClock, this.peerId, vertexId, key, value);
|
|
1103
1076
|
this.localOps.push(op);
|
|
1104
1077
|
this.applyProperty(op);
|
|
1105
1078
|
}
|
|
1079
|
+
/**
|
|
1080
|
+
* Promotes all transient (temporary) properties to persistent properties.
|
|
1081
|
+
* @param vertexId - The ID of the vertex to commit transients for.
|
|
1082
|
+
* @returns
|
|
1083
|
+
*/
|
|
1106
1084
|
commitTransients(vertexId) {
|
|
1107
1085
|
const vertex = this.state.getVertex(vertexId);
|
|
1108
1086
|
if (!vertex) {
|
|
@@ -1119,18 +1097,7 @@ var _RepTree = class _RepTree {
|
|
|
1119
1097
|
}
|
|
1120
1098
|
setVertexProperty(vertexId, key, value) {
|
|
1121
1099
|
this.lamportClock++;
|
|
1122
|
-
|
|
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);
|
|
1100
|
+
const op = newSetVertexPropertyOp(this.lamportClock, this.peerId, vertexId, key, value);
|
|
1134
1101
|
this.localOps.push(op);
|
|
1135
1102
|
this.applyProperty(op);
|
|
1136
1103
|
}
|
|
@@ -1293,13 +1260,7 @@ var _RepTree = class _RepTree {
|
|
|
1293
1260
|
if (!propB) {
|
|
1294
1261
|
return false;
|
|
1295
1262
|
}
|
|
1296
|
-
if (propA.value
|
|
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) {
|
|
1263
|
+
if (propA.value !== propB.value) {
|
|
1303
1264
|
return false;
|
|
1304
1265
|
}
|
|
1305
1266
|
}
|
|
@@ -1396,40 +1357,6 @@ var _RepTree = class _RepTree {
|
|
|
1396
1357
|
this.state.setTransientProperty(op.targetId, op.key, op.value);
|
|
1397
1358
|
this.reportOpAsApplied(op);
|
|
1398
1359
|
}
|
|
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
1360
|
applyProperty(op) {
|
|
1434
1361
|
const targetVertex = this.state.getVertex(op.targetId);
|
|
1435
1362
|
if (!targetVertex) {
|
|
@@ -1443,11 +1370,7 @@ var _RepTree = class _RepTree {
|
|
|
1443
1370
|
return;
|
|
1444
1371
|
}
|
|
1445
1372
|
this.updateLamportClock(op);
|
|
1446
|
-
|
|
1447
|
-
this.applyModifyProperty(op, targetVertex);
|
|
1448
|
-
} else {
|
|
1449
|
-
this.applyLLWProperty(op, targetVertex);
|
|
1450
|
-
}
|
|
1373
|
+
this.applyLLWProperty(op, targetVertex);
|
|
1451
1374
|
}
|
|
1452
1375
|
applyLLWProperty(op, targetVertex) {
|
|
1453
1376
|
const prevTransientOpId = this.transientPropertiesAndTheirOpIds.get(`${op.key}@${op.targetId}`);
|
|
@@ -1469,28 +1392,7 @@ var _RepTree = class _RepTree {
|
|
|
1469
1392
|
}
|
|
1470
1393
|
}
|
|
1471
1394
|
}
|
|
1472
|
-
|
|
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
|
-
}
|
|
1395
|
+
// Non-LWW modify-property flow removed
|
|
1494
1396
|
applyOperation(op) {
|
|
1495
1397
|
if (isMoveVertexOp(op)) {
|
|
1496
1398
|
this.applyMove(op);
|
|
@@ -1616,8 +1518,6 @@ var RepTree = _RepTree;
|
|
|
1616
1518
|
bindVertex,
|
|
1617
1519
|
defaultAliases,
|
|
1618
1520
|
isAnyPropertyOp,
|
|
1619
|
-
isLWWPropertyOp,
|
|
1620
|
-
isModifyPropertyOp,
|
|
1621
1521
|
isMoveVertexOp,
|
|
1622
1522
|
newMoveVertexOp,
|
|
1623
1523
|
newSetTransientVertexPropertyOp,
|