@cr_docs_t/dts 0.23.0 → 0.25.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.
Files changed (61) hide show
  1. package/dist/dts/Fugue/FNode.d.ts +1 -1
  2. package/dist/dts/Fugue/FNode.d.ts.map +1 -1
  3. package/dist/dts/Fugue/FNode.js +1 -1
  4. package/dist/dts/Fugue/FugueList.d.ts +1 -1
  5. package/dist/dts/Fugue/FugueList.d.ts.map +1 -1
  6. package/dist/dts/Fugue/FugueList.js +20 -12
  7. package/dist/dts/Fugue/index.d.ts +0 -1
  8. package/dist/dts/Fugue/index.d.ts.map +1 -1
  9. package/dist/dts/Fugue/index.js +1 -1
  10. package/dist/dts/FugueTree/FTree.d.ts +147 -0
  11. package/dist/dts/FugueTree/FTree.d.ts.map +1 -0
  12. package/dist/dts/FugueTree/FTree.js +467 -0
  13. package/dist/dts/FugueTree/FugueTree.d.ts +127 -0
  14. package/dist/dts/FugueTree/FugueTree.d.ts.map +1 -0
  15. package/dist/dts/FugueTree/FugueTree.js +349 -0
  16. package/dist/dts/FugueTree/index.d.ts +3 -0
  17. package/dist/dts/FugueTree/index.d.ts.map +1 -0
  18. package/dist/dts/FugueTree/index.js +2 -0
  19. package/dist/dts/Serailizers/Fugue/Message.d.ts +1 -1
  20. package/dist/dts/Serailizers/Fugue/Message.d.ts.map +1 -1
  21. package/dist/dts/Serailizers/Fugue/Message.js +13 -4
  22. package/dist/dts/Serailizers/Fugue/State.d.ts +1 -1
  23. package/dist/dts/Serailizers/Fugue/State.d.ts.map +1 -1
  24. package/dist/dts/Serailizers/FugueTree/Message.d.ts +13 -0
  25. package/dist/dts/Serailizers/FugueTree/Message.d.ts.map +1 -0
  26. package/dist/dts/Serailizers/FugueTree/Message.js +21 -0
  27. package/dist/dts/Serailizers/FugueTree/State.d.ts +9 -0
  28. package/dist/dts/Serailizers/FugueTree/State.d.ts.map +1 -0
  29. package/dist/dts/Serailizers/FugueTree/State.js +16 -0
  30. package/dist/dts/Serailizers/FugueTree/index.d.ts +3 -0
  31. package/dist/dts/Serailizers/FugueTree/index.d.ts.map +1 -0
  32. package/dist/dts/Serailizers/FugueTree/index.js +2 -0
  33. package/dist/dts/Serailizers/index.d.ts +1 -1
  34. package/dist/dts/Serailizers/index.d.ts.map +1 -1
  35. package/dist/dts/Serailizers/index.js +2 -1
  36. package/dist/dts/TotalOrder/StringTotalOrder.d.ts +2 -2
  37. package/dist/dts/TotalOrder/StringTotalOrder.d.ts.map +1 -1
  38. package/dist/dts/index.d.ts +2 -1
  39. package/dist/dts/index.d.ts.map +1 -1
  40. package/dist/dts/index.js +2 -1
  41. package/dist/tests/FugueTree.test.d.ts +2 -0
  42. package/dist/tests/FugueTree.test.d.ts.map +1 -0
  43. package/dist/tests/FugueTree.test.js +8 -0
  44. package/dist/tests/mocks.d.ts +2 -1
  45. package/dist/tests/mocks.d.ts.map +1 -1
  46. package/dist/tests/mocks.js +2 -1
  47. package/dist/types/Fugue/Fugue.d.ts +2 -2
  48. package/dist/types/Fugue/Fugue.d.ts.map +1 -1
  49. package/dist/types/Fugue/Message.d.ts +1 -0
  50. package/dist/types/Fugue/Message.d.ts.map +1 -1
  51. package/dist/types/FugueTree/Message.d.ts +44 -0
  52. package/dist/types/FugueTree/Message.d.ts.map +1 -0
  53. package/dist/types/FugueTree/Message.js +8 -0
  54. package/dist/types/FugueTree/index.d.ts +2 -0
  55. package/dist/types/FugueTree/index.d.ts.map +1 -0
  56. package/dist/types/FugueTree/index.js +1 -0
  57. package/dist/types/index.d.ts +1 -1
  58. package/dist/types/index.d.ts.map +1 -1
  59. package/dist/types/index.js +2 -1
  60. package/dist/utils/index.d.ts.map +1 -1
  61. package/package.json +1 -1
@@ -0,0 +1,467 @@
1
+ /**
2
+ * FTree is a CRDT for collaborative text editing. It represents the document as a tree of nodes,
3
+ * where each node corresponds to a character. The tree structure encodes the relative ordering
4
+ * of characters, and each node has a unique ID that allows it to be referenced by other nodes.
5
+ * The tree supports insertions and deletions of characters, and can be traversed in order to reconstruct the document text.
6
+ *
7
+ */
8
+ export class FTree {
9
+ constructor() {
10
+ // Map from sender ID to an array of nodes created by that sender, indexed by counter. This allows for efficient lookup of nodes by ID.
11
+ this.nodes = new Map();
12
+ this.root = {
13
+ id: { sender: "", counter: 0 },
14
+ value: null,
15
+ isDeleted: true,
16
+ parent: null,
17
+ side: "R",
18
+ leftChildren: [],
19
+ rightChildren: [],
20
+ size: 0,
21
+ };
22
+ this.nodes.set("", [this.root]);
23
+ }
24
+ /**
25
+ * Get the node with the given ID. Throws an error if no such node exists.
26
+ * @param id - the ID of the node to retrieve
27
+ * @returns the node with the given ID
28
+ */
29
+ getByID(id) {
30
+ const sender = this.nodes.get(id.sender);
31
+ if (sender !== undefined) {
32
+ const node = sender[id.counter];
33
+ if (node !== undefined)
34
+ return node;
35
+ }
36
+ throw new Error(`Unknown ID ${JSON.stringify(id)}`);
37
+ }
38
+ /**
39
+ * Add a node to the tree with the given ID, value, parent, side, and optional rightOrigin.
40
+ * The node is inserted among its siblings according to the order defined by rightOrigin and sender ID,
41
+ * and the size of all ancestors is updated accordingly.
42
+ * @param id - the ID of the new node
43
+ * @param value - the character value of the new node
44
+ * @param parent - the parent node of the new node
45
+ * @param side - the side (left or right) of the new node with respect to its parent
46
+ * @param rightOriginID - the ID of the node to the right of which the new node was inserted, or null if inserted at the end of its siblings (optional)
47
+ */
48
+ addNode(id, value, parent, side, rightOriginID) {
49
+ const node = {
50
+ id,
51
+ value,
52
+ isDeleted: false,
53
+ parent,
54
+ side,
55
+ leftChildren: [],
56
+ rightChildren: [],
57
+ size: 0,
58
+ };
59
+ // If a rightOriginID is present we set the rightOrigin pointer.
60
+ // We require that rightOrigin is already in the tree, since it
61
+ // must be a sibling of the new node and thus must have been inserted before it.
62
+ if (rightOriginID !== undefined) {
63
+ node.rightOrigin = rightOriginID === null ? null : this.getByID(rightOriginID);
64
+ }
65
+ // Add node to nodesByID for lookup by ID.
66
+ let sender = this.nodes.get(id.sender);
67
+ if (!sender) {
68
+ sender = [];
69
+ this.nodes.set(id.sender, sender);
70
+ }
71
+ sender.push(node);
72
+ // Insert node into siblings and update sizes of ancestors.
73
+ this.insertIntoSiblings(node);
74
+ this.updateSize(node, 1);
75
+ }
76
+ /**
77
+ * TODO: Maybe change the children types to linked list to make insertions more efficient, since we expect many insertions at the end of the siblings.
78
+ * Inserts a node into the correct position among its parent node's children arrays based on the
79
+ * node's rightOrigin and sender ID, and the existing order of its rightOrigin among its siblings.
80
+ * @param node - The node to insert into its siblings
81
+ */
82
+ insertIntoSiblings(node) {
83
+ // Insert node among its same-side siblings.
84
+ const p = node.parent;
85
+ if (node.side === "R") {
86
+ const right = p.rightChildren;
87
+ // We want to insert node in the rightChildren array so that it is after all nodes that are less than
88
+ // node according to the following order:
89
+ // - If a and b have different rightOrigins, the one with the lesser rightOrigin goes first with null < any node.
90
+ // - If a ad b have the same rightOrigin, the one with the lesser sender ID goes first
91
+ let i = 0;
92
+ for (; i < right.length; i++) {
93
+ const sib = right[i];
94
+ const isLessThanSib = this.isLess(node.rightOrigin, sib.rightOrigin);
95
+ const isEqualRightOrigin = node.rightOrigin === sib.rightOrigin;
96
+ const isGreaterSender = node.id.sender > sib.id.sender;
97
+ if (!(isLessThanSib || (isEqualRightOrigin && isGreaterSender)))
98
+ break;
99
+ }
100
+ right.splice(i, 0, node);
101
+ }
102
+ else {
103
+ const left = p.leftChildren;
104
+ // We want to insert node in the leftChildren array so that it is after all nodes that are have a lexographically
105
+ // less sender ID, since all nodes in the leftChildren array have the same rightOrigin (the parent node).
106
+ let i = 0;
107
+ for (; i < left.length; i++) {
108
+ if (!(node.id.sender.localeCompare(left[i].id.sender) > 0))
109
+ break;
110
+ }
111
+ left.splice(i, 0, node);
112
+ }
113
+ }
114
+ /**
115
+ * Determines whether node a should come before node b in the document order, based on their rightOrigin and sender ID.
116
+ * returns true if a should come before b, false otherwise. i.e. a < b
117
+ * @param a - the first node to compare
118
+ * @param b - the second node to compare
119
+ */
120
+ isLess(a, b) {
121
+ if (a === b)
122
+ return false;
123
+ if (a === null)
124
+ return false;
125
+ if (b === null)
126
+ return true;
127
+ // Walk one node up the tree until they are both the same depth.
128
+ const aDepth = this.depth(a);
129
+ const bDepth = this.depth(b);
130
+ let aAn = a;
131
+ let bAn = b;
132
+ if (aDepth > bDepth) {
133
+ let lastSide;
134
+ for (let i = aDepth; i > bDepth; i--) {
135
+ lastSide = aAn.side;
136
+ aAn = aAn.parent;
137
+ }
138
+ if (aAn === b) {
139
+ // a is a descendant of b on lastSide.
140
+ return lastSide === "L";
141
+ }
142
+ }
143
+ if (bDepth > aDepth) {
144
+ let lastSide;
145
+ for (let i = bDepth; i > aDepth; i--) {
146
+ lastSide = bAn.side;
147
+ bAn = bAn.parent;
148
+ }
149
+ if (bAn === a) {
150
+ // b is a descendant of a on lastSide.
151
+ return lastSide === "R";
152
+ }
153
+ }
154
+ // Walk both nodes up the tree until we find a common ancestor.
155
+ while (aAn.parent !== bAn.parent) {
156
+ // If we reach the root, the loop will terminate, so both parents
157
+ // are non-null here.
158
+ aAn = aAn.parent;
159
+ bAn = bAn.parent;
160
+ }
161
+ // Now aAn and bAn are distinct siblings. See how they are sorted
162
+ // in their parent's child arrays.
163
+ if (aAn.side !== bAn.side)
164
+ return aAn.side === "L";
165
+ else {
166
+ const siblings = aAn.side === "L" ? aAn.parent.leftChildren : aAn.parent.rightChildren;
167
+ return siblings.indexOf(aAn) < siblings.indexOf(bAn);
168
+ }
169
+ }
170
+ /**
171
+ * Find the depth of a node in the tree, defined as the number of edges from the node to the root. The root has depth 0.
172
+ * @param node - the node whose depth to calculate
173
+ * @returns the depth of the node in the tree
174
+ */
175
+ depth(node) {
176
+ let d = 0;
177
+ let n = node;
178
+ while (n.parent !== null) {
179
+ d++;
180
+ n = n.parent;
181
+ }
182
+ return d;
183
+ }
184
+ /**
185
+ * Update the size of a node and all its ancestors by adding delta.
186
+ * This should be called whenever a node is inserted or deleted to keep the size values accurate.
187
+ * @param node - the node whose size and ancestors' sizes to update
188
+ * @param delta - the amount to add to the size of the node and its ancestors (positive for insertions, negative for deletions)
189
+ */
190
+ updateSize(node, delta) {
191
+ let an = node;
192
+ while (an !== null) {
193
+ an.size += delta;
194
+ an = an.parent;
195
+ }
196
+ }
197
+ /**
198
+ * Get the node by index from a starting node, i.e. the node corresponding to the index-th non-deleted
199
+ * character in the subtree rooted at the starting node, where indices are 0-based.
200
+ * @param node - node to start the search from (e.g. the root for document-level indexing)
201
+ * @param index - the index of the node to retrieve among the non-deleted nodes in the subtree rooted at the starting node
202
+ * @returns the node corresponding to the index-th non-deleted character in the subtree rooted at the starting node
203
+ */
204
+ getByIndex(node, index) {
205
+ if (index < 0 || index >= node.size) {
206
+ throw new Error(`Index out of bounds: ${index}`);
207
+ }
208
+ // Inorder traversal of the subtree, but using the size values to skip over deleted nodes and entire subtrees that are before the index.
209
+ let rem = index;
210
+ rec: while (true) {
211
+ for (const child of node.leftChildren) {
212
+ if (rem < child.size) {
213
+ node = child;
214
+ continue rec;
215
+ }
216
+ rem -= child.size;
217
+ }
218
+ if (!node.isDeleted) {
219
+ // If the current node is not deleted and rem is 0, we are at the node we want
220
+ // otherwise keep traversing, decrementing rem by 1 to account for the current node.
221
+ if (rem === 0)
222
+ return node;
223
+ rem--;
224
+ }
225
+ for (const child of node.rightChildren) {
226
+ if (rem < child.size) {
227
+ node = child;
228
+ continue rec;
229
+ }
230
+ rem -= child.size;
231
+ }
232
+ throw new Error("Index in range but not found");
233
+ }
234
+ }
235
+ /**
236
+ * Get the index of a node disregarding the deleted nodes starting from the root
237
+ * @param node - the node whose index to calculate among the non-deleted nodes in the subtree rooted at the root
238
+ * @returns the index of the node among the non-deleted nodes in the subtree rooted at the root
239
+ */
240
+ getVisibleIndex(node) {
241
+ let index = 0;
242
+ // Add the size of all visible nodes in our own left-side subtrees
243
+ // since they come before us in the document (inorder) order.
244
+ for (const left of node.leftChildren) {
245
+ index += left.size;
246
+ }
247
+ let curr = node;
248
+ while (curr.parent !== null) {
249
+ const parent = curr.parent;
250
+ if (curr.side === "R") {
251
+ // If we are on the right side of our parent:
252
+ // Everything in parent's leftChildren is before us
253
+ for (const left of parent.leftChildren)
254
+ index += left.size;
255
+ // The parent itself is before us (if not deleted)
256
+ if (!parent.isDeleted)
257
+ index += 1;
258
+ // Every right-sibling that is to our left in the array is before us
259
+ const sibIdx = parent.rightChildren.indexOf(curr);
260
+ for (let i = 0; i < sibIdx; i++) {
261
+ index += parent.rightChildren[i].size;
262
+ }
263
+ }
264
+ else {
265
+ // If we are on the left side of our parent:
266
+ // Only siblings to our left in the leftChildren array are before us
267
+ const sibIdx = parent.leftChildren.indexOf(curr);
268
+ for (let i = 0; i < sibIdx; i++) {
269
+ index += parent.leftChildren[i].size;
270
+ }
271
+ }
272
+ curr = parent;
273
+ }
274
+ return index;
275
+ }
276
+ /**
277
+ * Get the leftmost descendant of a node, i.e. the node corresponding to the first non-deleted character in the subtree
278
+ * rooted at the given node in document order.
279
+ * @param node - the node whose leftmost descendant to find
280
+ * @returns the leftmost descendant of the given node
281
+ */
282
+ leftmostDescendant(node) {
283
+ let desc = node;
284
+ while (desc.leftChildren.length !== 0)
285
+ desc = desc.leftChildren[0];
286
+ return desc;
287
+ }
288
+ /**
289
+ * Get the next non-descendant of a node, i.e. the node corresponding to the next non-deleted character in document order
290
+ * that is not in the subtree rooted at the given node.
291
+ * @param node - the node whose next non-descendant to find
292
+ * @returns the next non-descendant of the given node, or null if there is no such node (i.e. the given node is the last non-deleted character in document order)
293
+ */
294
+ nextNonDescendant(node) {
295
+ let current = node;
296
+ while (current.parent !== null) {
297
+ const siblings = current.side === "L" ? current.parent.leftChildren : current.parent.rightChildren;
298
+ const index = siblings.indexOf(current);
299
+ if (index < siblings.length - 1) {
300
+ // The next sibling's subtree immediately follows current's subtree.
301
+ // Find its leftmost element.
302
+ const nextSibling = siblings[index + 1];
303
+ return this.leftmostDescendant(nextSibling);
304
+ }
305
+ else if (current.side === "L") {
306
+ // The parent immediately follows current's subtree.
307
+ return current.parent;
308
+ }
309
+ current = current.parent;
310
+ }
311
+ // We've reached the root without finding any further-right subtrees.
312
+ return null;
313
+ }
314
+ /**
315
+ * Traverse the subtree rooted at a node in document order, yielding the value of each non-deleted node.
316
+ * @param node - the node to traverse from
317
+ */
318
+ *traverse(node) {
319
+ let current = node;
320
+ // Stack records the next child to visit for that node.
321
+ // We don't need to store node because we can infer it from the
322
+ // current node's parent etc.
323
+ const S = [{ side: "L", childIndex: 0 }];
324
+ while (true) {
325
+ const top = S[S.length - 1];
326
+ const children = top.side === "L" ? current.leftChildren : current.rightChildren;
327
+ if (top.childIndex === children.length) {
328
+ // We are done with the children on top.side.
329
+ if (top.side === "L") {
330
+ // Visit current, then move to right children.
331
+ if (!current.isDeleted)
332
+ yield current.value;
333
+ top.side = "R";
334
+ top.childIndex = 0;
335
+ }
336
+ else {
337
+ // Go to the parent.
338
+ if (current.parent === null)
339
+ return;
340
+ current = current.parent;
341
+ S.pop();
342
+ }
343
+ }
344
+ else {
345
+ const child = children[top.childIndex];
346
+ // Save for later that we need to visit the next child.
347
+ top.childIndex++;
348
+ if (child.size > 0) {
349
+ // Traverse child.
350
+ current = child;
351
+ S.push({ side: "L", childIndex: 0 });
352
+ }
353
+ }
354
+ }
355
+ }
356
+ /**
357
+ * Serialize the tree into a Uint8Array. The tree is converted into a JSON object where each node is
358
+ * represented by its value, isDeleted flag, parent ID, side, size, and rightOrigin ID (if applicable).
359
+ * The JSON object is then converted to a string and encoded as a Uint8Array for storage or transmission.
360
+ * @returns a Uint8Array containing the serialized tree data
361
+ */
362
+ save() {
363
+ // Convert nodesByID into JSON format, also converting each Node into a NodeSave.
364
+ const save = {};
365
+ for (const [sender, bySender] of this.nodes) {
366
+ save[sender] = bySender.map((node) => {
367
+ const nodeSave = {
368
+ value: node.value,
369
+ isDeleted: node.isDeleted,
370
+ parent: node.parent === null ? null : node.parent.id,
371
+ side: node.side,
372
+ size: node.size,
373
+ };
374
+ if (node.rightOrigin !== undefined) {
375
+ nodeSave.rightOrigin = node.rightOrigin === null ? null : node.rightOrigin.id;
376
+ }
377
+ return nodeSave;
378
+ });
379
+ }
380
+ return new Uint8Array(Buffer.from(JSON.stringify(save)));
381
+ }
382
+ /**
383
+ * Load the tree from a Uint8Array containing serialized tree data in the format produced by the save() method.
384
+ * The data is parsed from JSON format, and the tree is reconstructed by first creating all nodes without setting their parent or rightOrigin pointers,
385
+ * then filling in the parent and rightOrigin pointers, and finally calling insertIntoSiblings on each node to reconstruct the children arrays.
386
+ * @param saveData - a Uint8Array containing the serialized tree data to load
387
+ */
388
+ load(saveData) {
389
+ const save = JSON.parse(new TextDecoder().decode(saveData));
390
+ // First create all nodes without pointers to other nodes (parent, children,
391
+ // rightOrigin).
392
+ for (const [sender, bySenderSave] of Object.entries(save)) {
393
+ if (sender === "") {
394
+ // Root node. Just set its size.
395
+ this.root.size = bySenderSave[0].size;
396
+ continue;
397
+ }
398
+ this.nodes.set(sender, bySenderSave.map((nodeSave, counter) => ({
399
+ id: { sender, counter },
400
+ parent: null,
401
+ value: nodeSave.value,
402
+ isDeleted: nodeSave.isDeleted,
403
+ side: nodeSave.side,
404
+ size: nodeSave.size,
405
+ leftChildren: [],
406
+ rightChildren: [],
407
+ })));
408
+ }
409
+ // Next, fill in the parent and rightOrigin pointers.
410
+ for (const [sender, bySender] of this.nodes) {
411
+ if (sender === "")
412
+ continue;
413
+ const bySenderSave = save[sender];
414
+ for (let i = 0; i < bySender.length; i++) {
415
+ const node = bySender[i];
416
+ const nodeSave = bySenderSave[i];
417
+ if (nodeSave.parent !== null) {
418
+ node.parent = this.getByID(nodeSave.parent);
419
+ }
420
+ if (nodeSave.rightOrigin !== undefined) {
421
+ node.rightOrigin = nodeSave.rightOrigin === null ? null : this.getByID(nodeSave.rightOrigin);
422
+ }
423
+ }
424
+ }
425
+ // Finally, call insertIntoSiblings on each node to fill in the children
426
+ // arrays.
427
+ // We must be careful to wait until after doing so for node.rightOrigin
428
+ // and its ancestors, since insertIntoSiblings references the existing list order
429
+ // on node.rightOrigin.
430
+ // Nodes go from "pending" -> "ready" (rightOrigin valid) ->
431
+ // "valid" (insertIntoSiblings called).
432
+ // readyNodes is a stack; pendingNodes maps from a node to its dependencies.
433
+ const readyNodes = [];
434
+ const pendingNodes = new Map();
435
+ for (const [sender, bySender] of this.nodes) {
436
+ if (sender === "")
437
+ continue;
438
+ for (let i = 0; i < bySender.length; i++) {
439
+ const node = bySender[i];
440
+ if (node.rightOrigin === undefined || node.rightOrigin === null) {
441
+ // rightOrigin not used or is the root; node is ready.
442
+ readyNodes.push(node);
443
+ }
444
+ else {
445
+ let pendingArr = pendingNodes.get(node.rightOrigin);
446
+ if (pendingArr === undefined) {
447
+ pendingArr = [];
448
+ pendingNodes.set(node.rightOrigin, pendingArr);
449
+ }
450
+ pendingArr.push(node);
451
+ }
452
+ }
453
+ }
454
+ while (readyNodes.length !== 0) {
455
+ const node = readyNodes.pop();
456
+ this.insertIntoSiblings(node);
457
+ // node's dependencies are now ready.
458
+ const deps = pendingNodes.get(node);
459
+ if (deps !== undefined)
460
+ readyNodes.push(...deps);
461
+ pendingNodes.delete(node);
462
+ }
463
+ if (pendingNodes.size !== 0) {
464
+ throw new Error("Failed to validate all nodes");
465
+ }
466
+ }
467
+ }
@@ -0,0 +1,127 @@
1
+ import { FugueMessage } from "../../types/FugueTree/Message.js";
2
+ import { FNode, ID } from "./FTree.js";
3
+ /**
4
+ * A Fugue Tree CRDT, with insert and delete operations.
5
+ */
6
+ export declare class FugueTree {
7
+ private counter;
8
+ private tree;
9
+ ws: WebSocket | null;
10
+ documentID: string;
11
+ readonly replicaID: string;
12
+ userIdentity: string | undefined;
13
+ pendingMsgs: Map<string, FugueMessage>;
14
+ readonly batchSize = 100;
15
+ constructor(ws: WebSocket | null, documentID: string, userIdentity?: string);
16
+ /**
17
+ * Make msg key for pending messages map
18
+ * @param msg - the message to make key for
19
+ * @returns the key for the message in pending messages map
20
+ */
21
+ private makeMsgKey;
22
+ /**
23
+ * Propagates message or messages to replicas
24
+ * @param msg - Message or messages to propagate to replicas
25
+ */
26
+ private propagate;
27
+ /**
28
+ * Inserts a value at the given index and returns the corresponding FugueMessage.
29
+ * @param index - the index to insert the value at
30
+ * @param value - the value to insert
31
+ * @returns the FugueMessage representing the insert operation
32
+ */
33
+ private insertImpl;
34
+ /**
35
+ * Inserts multiple values starting at the given index. This is optimized for batch inserts, such as pasting a large chunk of text.
36
+ * @param index - the index to start inserting values at
37
+ * @param values - the string of values to insert, where each character is inserted as a separate node in the tree
38
+ */
39
+ insertMultiple(index: number, values: string): FugueMessage[];
40
+ /**
41
+ * Inserts a value at the given index and propagates the corresponding FugueMessage to replicas.
42
+ * @param index - the index to insert the value at
43
+ * @param value - the value to insert
44
+ */
45
+ insert(index: number, value: string): void;
46
+ /**
47
+ * Deletes the value at the given index and returns the corresponding FugueMessage.
48
+ * @param index - the index to delete the value at
49
+ * @returns the FugueMessage representing the delete operation
50
+ */
51
+ private deleteImpl;
52
+ /**
53
+ * Deletes multiple values starting at the given index. This is optimized for batch deletes, such as deleting a large chunk of text.
54
+ * @param index - the index to start deleting values at
55
+ * @param length - the number of characters to delete, starting from the index
56
+ */
57
+ deleteMultiple(index: number, length: number): FugueMessage[];
58
+ /**
59
+ * Deletes the value at the given index and propagates the corresponding FugueMessage to replicas.
60
+ * @param index - the index to delete the value at
61
+ */
62
+ delete(index: number): void;
63
+ /**
64
+ * Applies a FugueMessage to the tree. Returns true if the message was successfully applied,
65
+ * or false if it could not be applied due to missing dependencies (e.g. parent node for an insert).
66
+ * @param msg - the FugueMessage to apply to the tree
67
+ */
68
+ private applyToTree;
69
+ /**
70
+ * Processes pending messages that may now be applicable after applying new messages.
71
+ * This is called after successfully applying new messages, to check if any pending messages can now be applied due to their dependencies being satisfied.
72
+ * @param applied - the list of messages that were just applied, which may have satisfied dependencies for pending messages
73
+ */
74
+ private processPending;
75
+ /**
76
+ * Applies effect messages to the list
77
+ * @param msg - Message or messages to apply effect for, can be batched
78
+ * @returns the list of messages that were successfully applied
79
+ */
80
+ effect(msg: FugueMessage | FugueMessage[]): FugueMessage[];
81
+ /**
82
+ * Gets the value at the given index in the visible string.
83
+ * @param index - the index to get the value at, where the index is based on the visible string
84
+ * @returns the value at the given index in the visible string
85
+ */
86
+ get(index: number): string;
87
+ /**
88
+ * Gets the length of the visible string, which is the number of non-deleted nodes in the tree.
89
+ * @returns the length of the visible string
90
+ */
91
+ length(): number;
92
+ /**
93
+ * Returns the visible string by traversing the tree and concatenating the values of non-deleted nodes.
94
+ * @returns the visible string represented by the tree, which is the concatenation of values of non-deleted nodes in traversal order
95
+ */
96
+ observe(): string;
97
+ /**
98
+ * Serializes the tree into a Uint8Array.
99
+ * @returns a Uint8Array representing the serialized tree.
100
+ */
101
+ save(): Uint8Array;
102
+ /**
103
+ * Loads the tree from a Uint8Array. This replaces the current tree with the loaded tree.
104
+ * @param data - a Uint8Array representing the serialized tree to load.
105
+ */
106
+ load(data: Uint8Array | null): void;
107
+ /**
108
+ * Gets the replica ID of this FugueTree instance, which is a unique identifier for this replica in the distributed system.
109
+ * The replica ID is used in messages to identify the source of operations and to ensure that operations from the same replica
110
+ * are applied in order.
111
+ * @returns
112
+ */
113
+ replicaId(): string;
114
+ /**
115
+ * Gets the FNode corresponding to the given ID.
116
+ * @param id - the ID of the node to retrieve
117
+ * @returns the FNode corresponding to the given ID, or null if no such node exists in the tree
118
+ */
119
+ getById(id: ID): FNode;
120
+ /**
121
+ * Gets the index of the given node in the visible string.
122
+ * @param node - the FNode to get the visible index of
123
+ * @returns the index of the given node in the visible string
124
+ */
125
+ getVisibleIndex(node: FNode): number;
126
+ }
127
+ //# sourceMappingURL=FugueTree.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"FugueTree.d.ts","sourceRoot":"","sources":["../../../src/dts/FugueTree/FugueTree.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,YAAY,EAAa,MAAM,kCAAkC,CAAC;AAG3E,OAAO,EAAE,KAAK,EAAS,EAAE,EAAE,MAAM,YAAY,CAAC;AAE9C;;GAEG;AACH,qBAAa,SAAS;IAClB,OAAO,CAAC,OAAO,CAAK;IACpB,OAAO,CAAC,IAAI,CAAQ;IACpB,EAAE,EAAE,SAAS,GAAG,IAAI,CAAC;IACrB,UAAU,EAAE,MAAM,CAAC;IACnB,QAAQ,CAAC,SAAS,SAAmB;IACrC,YAAY,EAAE,MAAM,GAAG,SAAS,CAAC;IACjC,WAAW,4BAAmC;IAE9C,QAAQ,CAAC,SAAS,OAAO;gBAEb,EAAE,EAAE,SAAS,GAAG,IAAI,EAAE,UAAU,EAAE,MAAM,EAAE,YAAY,CAAC,EAAE,MAAM;IAO3E;;;;OAIG;IACH,OAAO,CAAC,UAAU;IAIlB;;;OAGG;IACH,OAAO,CAAC,SAAS;IAQjB;;;;;OAKG;IACH,OAAO,CAAC,UAAU;IA6ClB;;;;OAIG;IACH,cAAc,CAAC,KAAK,EAAE,MAAM,EAAE,MAAM,EAAE,MAAM;IA+B5C;;;;OAIG;IACH,MAAM,CAAC,KAAK,EAAE,MAAM,EAAE,KAAK,EAAE,MAAM;IAOnC;;;;OAIG;IACH,OAAO,CAAC,UAAU;IAsBlB;;;;OAIG;IACH,cAAc,CAAC,KAAK,EAAE,MAAM,EAAE,MAAM,EAAE,MAAM;IA4B5C;;;OAGG;IACH,MAAM,CAAC,KAAK,EAAE,MAAM;IAKpB;;;;OAIG;IACH,OAAO,CAAC,WAAW;IA8BnB;;;;OAIG;IACH,OAAO,CAAC,cAAc;IAkBtB;;;;OAIG;IACH,MAAM,CAAC,GAAG,EAAE,YAAY,GAAG,YAAY,EAAE,GAAG,YAAY,EAAE;IAuB1D;;;;OAIG;IACH,GAAG,CAAC,KAAK,EAAE,MAAM,GAAG,MAAM;IAS1B;;;OAGG;IACH,MAAM,IAAI,MAAM;IAIhB;;;OAGG;IACH,OAAO,IAAI,MAAM;IAWjB;;;OAGG;IACH,IAAI,IAAI,UAAU;IAKlB;;;OAGG;IACH,IAAI,CAAC,IAAI,EAAE,UAAU,GAAG,IAAI;IAK5B;;;;;OAKG;IACH,SAAS,IAAI,MAAM;IAInB;;;;OAIG;IACH,OAAO,CAAC,EAAE,EAAE,EAAE;IAId;;;;OAIG;IACH,eAAe,CAAC,IAAI,EAAE,KAAK;CAG9B"}