reptree 0.4.0-alpha.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 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