@cr_docs_t/dts 0.0.9-alpha.0 → 0.0.10
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/CausalTree/CTNode.d.ts +1 -2
- package/dist/CausalTree/CTNode.d.ts.map +1 -1
- package/dist/CausalTree/CTNode.js +1 -2
- package/dist/CausalTree/CausalTree.d.ts +2 -3
- package/dist/CausalTree/CausalTree.d.ts.map +1 -1
- package/dist/CausalTree/CausalTree.js +1 -2
- package/dist/Fugue/FNode.d.ts +1 -2
- package/dist/Fugue/FNode.d.ts.map +1 -1
- package/dist/Fugue/FNode.js +1 -2
- package/dist/Fugue/FugueList.d.ts +38 -4
- package/dist/Fugue/FugueList.d.ts.map +1 -1
- package/dist/Fugue/FugueList.js +132 -52
- package/dist/TotalOrder/StringTotalOrder.d.ts +4 -2
- package/dist/TotalOrder/StringTotalOrder.d.ts.map +1 -1
- package/dist/TotalOrder/StringTotalOrder.js +6 -3
- package/dist/TotalOrder/UniquelyDenseTotalOrder.d.ts +2 -1
- package/dist/TotalOrder/UniquelyDenseTotalOrder.d.ts.map +1 -1
- package/dist/index.d.ts +6 -7
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +6 -7
- package/dist/types/Fugue.d.ts +1 -1
- package/dist/types/Fugue.d.ts.map +1 -1
- package/dist/types/Message.d.ts +3 -3
- package/dist/types/Message.d.ts.map +1 -1
- package/package.json +1 -1
- package/dist/Fugue/FugueTest.d.ts +0 -2
- package/dist/Fugue/FugueTest.d.ts.map +0 -1
- package/dist/Fugue/FugueTest.js +0 -22
- package/dist/Fugue/utils.d.ts +0 -34
- package/dist/Fugue/utils.d.ts.map +0 -1
- package/dist/Fugue/utils.js +0 -41
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"CTNode.d.ts","sourceRoot":"","sources":["../../src/CausalTree/CTNode.ts"],"names":[],"mappings":"AAAA,
|
|
1
|
+
{"version":3,"file":"CTNode.d.ts","sourceRoot":"","sources":["../../src/CausalTree/CTNode.ts"],"names":[],"mappings":"AAAA,qBAAa,MAAM;IACf,GAAG,EAAE,MAAM,CAAC;IACZ,SAAS,CAAC,EAAE,MAAM,CAAC;IACnB,KAAK,CAAC,EAAE,MAAM,CAAC;gBAEH,GAAG,EAAE,MAAM;CAG1B"}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"CausalTree.d.ts","sourceRoot":"","sources":["../../src/CausalTree/CausalTree.ts"],"names":[],"mappings":"AAAA,OAAO,MAAM,MAAM,UAAU,CAAC;
|
|
1
|
+
{"version":3,"file":"CausalTree.d.ts","sourceRoot":"","sources":["../../src/CausalTree/CausalTree.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,MAAM,EAAE,MAAM,UAAU,CAAC;AAElC,qBAAa,UAAU;IAWnB,IAAI,EAAE,MAAM,CAAC;gBAED,IAAI,EAAE,MAAM;CAG3B"}
|
package/dist/Fugue/FNode.d.ts
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"FNode.d.ts","sourceRoot":"","sources":["../../src/Fugue/FNode.ts"],"names":[],"mappings":"AAAA,
|
|
1
|
+
{"version":3,"file":"FNode.d.ts","sourceRoot":"","sources":["../../src/Fugue/FNode.ts"],"names":[],"mappings":"AAAA,qBAAa,KAAK,CAAC,CAAC;IAChB,KAAK,CAAC,EAAE,MAAM,CAAC;IACf,QAAQ,EAAE,CAAC,CAAC;gBAEA,QAAQ,EAAE,CAAC,EAAE,KAAK,CAAC,EAAE,MAAM;CAI1C"}
|
package/dist/Fugue/FNode.js
CHANGED
|
@@ -1,29 +1,63 @@
|
|
|
1
1
|
import { FugueState } from "../types/Fugue";
|
|
2
|
-
import UniquelyDenseTotalOrder from "../TotalOrder/UniquelyDenseTotalOrder";
|
|
2
|
+
import { UniquelyDenseTotalOrder } from "../TotalOrder/UniquelyDenseTotalOrder";
|
|
3
3
|
import { FugueMessage } from "../types/Message";
|
|
4
4
|
/**
|
|
5
5
|
* A Fugue List CRDT, with insert and delete operations
|
|
6
6
|
*/
|
|
7
|
-
declare class FugueList<P> {
|
|
7
|
+
export declare class FugueList<P> {
|
|
8
8
|
state: FugueState<P>;
|
|
9
9
|
totalOrder: UniquelyDenseTotalOrder<P>;
|
|
10
10
|
positionCounter: number;
|
|
11
11
|
ws: WebSocket | null;
|
|
12
12
|
constructor(totalOrder: UniquelyDenseTotalOrder<P>, ws: WebSocket | null);
|
|
13
13
|
private propagate;
|
|
14
|
+
/**
|
|
15
|
+
* Inserts a value at a given position
|
|
16
|
+
* @param position - Position to insert at
|
|
17
|
+
* @param value - Value to insert
|
|
18
|
+
*/
|
|
19
|
+
private insertAtPosition;
|
|
20
|
+
/**
|
|
21
|
+
* Generates unique position for new element at 'index'
|
|
22
|
+
* @param index - Index to generate position for
|
|
23
|
+
* @returns Generated position
|
|
24
|
+
*/
|
|
25
|
+
private generatePosition;
|
|
14
26
|
/**
|
|
15
27
|
* Inserts new element with 'value' at 'index' in the list
|
|
16
28
|
* @param index - Index to insert 'value' at
|
|
17
29
|
* @param value - Value to insert
|
|
18
30
|
*/
|
|
19
31
|
insert(index: number, value: string): void;
|
|
32
|
+
/**
|
|
33
|
+
* Deletes value at given position
|
|
34
|
+
* @param position - Position to delete at
|
|
35
|
+
*/
|
|
36
|
+
private deleteAtPosition;
|
|
37
|
+
/**
|
|
38
|
+
* Finds the position of the visible value at index
|
|
39
|
+
* this ignores tombstoned values
|
|
40
|
+
* @param index - Index of the visible value
|
|
41
|
+
*/
|
|
42
|
+
findVisiblePosition(index: number): P | undefined;
|
|
43
|
+
/**
|
|
44
|
+
* Finds the visible index of the value at position
|
|
45
|
+
* this ignores tombstoned values
|
|
46
|
+
* @param position - Position to find visible index for
|
|
47
|
+
* @returns
|
|
48
|
+
*/
|
|
49
|
+
findVisibleIndex(position: P): number | undefined;
|
|
20
50
|
/**
|
|
21
51
|
* Delete value in the list at index
|
|
22
52
|
* @param index - Index of the value to delete
|
|
23
53
|
*/
|
|
24
54
|
delete(index: number): void;
|
|
55
|
+
/**
|
|
56
|
+
* Observes the current visible state of the list
|
|
57
|
+
* @returns The current visible state of the list as a string
|
|
58
|
+
*/
|
|
25
59
|
observe(): string;
|
|
26
|
-
effect(msg: FugueMessage): void;
|
|
60
|
+
effect(msg: FugueMessage<P>): void;
|
|
61
|
+
replicaId(): string;
|
|
27
62
|
}
|
|
28
|
-
export default FugueList;
|
|
29
63
|
//# sourceMappingURL=FugueList.d.ts.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"FugueList.d.ts","sourceRoot":"","sources":["../../src/Fugue/FugueList.ts"],"names":[],"mappings":"AACA,OAAO,EAAE,UAAU,EAAE,MAAM,gBAAgB,CAAC;AAC5C,OAAO,uBAAuB,MAAM,uCAAuC,CAAC;
|
|
1
|
+
{"version":3,"file":"FugueList.d.ts","sourceRoot":"","sources":["../../src/Fugue/FugueList.ts"],"names":[],"mappings":"AACA,OAAO,EAAE,UAAU,EAAE,MAAM,gBAAgB,CAAC;AAC5C,OAAO,EAAE,uBAAuB,EAAE,MAAM,uCAAuC,CAAC;AAChF,OAAO,EAAE,YAAY,EAAa,MAAM,kBAAkB,CAAC;AAE3D;;GAEG;AACH,qBAAa,SAAS,CAAC,CAAC;IACpB,KAAK,EAAE,UAAU,CAAC,CAAC,CAAC,CAAM;IAC1B,UAAU,EAAE,uBAAuB,CAAC,CAAC,CAAC,CAAC;IACvC,eAAe,SAAK;IACpB,EAAE,EAAE,SAAS,GAAG,IAAI,CAAC;gBAET,UAAU,EAAE,uBAAuB,CAAC,CAAC,CAAC,EAAE,EAAE,EAAE,SAAS,GAAG,IAAI;IAKxE,OAAO,CAAC,SAAS;IAMjB;;;;OAIG;IACH,OAAO,CAAC,gBAAgB;IAiCxB;;;;OAIG;IACH,OAAO,CAAC,gBAAgB;IASxB;;;;OAIG;IACH,MAAM,CAAC,KAAK,EAAE,MAAM,EAAE,KAAK,EAAE,MAAM;IAcnC;;;OAGG;IACH,OAAO,CAAC,gBAAgB;IAcxB;;;;OAIG;IACH,mBAAmB,CAAC,KAAK,EAAE,MAAM,GAAG,CAAC,GAAG,SAAS;IAajD;;;;;OAKG;IACH,gBAAgB,CAAC,QAAQ,EAAE,CAAC,GAAG,MAAM,GAAG,SAAS;IAejD;;;OAGG;IACH,MAAM,CAAC,KAAK,EAAE,MAAM;IAoBpB;;;OAGG;IACH,OAAO,IAAI,MAAM;IAmBjB,MAAM,CAAC,GAAG,EAAE,YAAY,CAAC,CAAC,CAAC;IAc3B,SAAS,IAAI,MAAM;CAGtB"}
|
package/dist/Fugue/FugueList.js
CHANGED
|
@@ -1,9 +1,9 @@
|
|
|
1
|
-
import FNode from "./FNode";
|
|
1
|
+
import { FNode } from "./FNode";
|
|
2
2
|
import { Operation } from "../types/Message";
|
|
3
3
|
/**
|
|
4
4
|
* A Fugue List CRDT, with insert and delete operations
|
|
5
5
|
*/
|
|
6
|
-
class FugueList {
|
|
6
|
+
export class FugueList {
|
|
7
7
|
constructor(totalOrder, ws) {
|
|
8
8
|
this.state = [];
|
|
9
9
|
this.positionCounter = 0;
|
|
@@ -16,93 +16,173 @@ class FugueList {
|
|
|
16
16
|
this.ws.send(JSON.stringify(msg));
|
|
17
17
|
}
|
|
18
18
|
/**
|
|
19
|
-
* Inserts
|
|
20
|
-
* @param
|
|
19
|
+
* Inserts a value at a given position
|
|
20
|
+
* @param position - Position to insert at
|
|
21
21
|
* @param value - Value to insert
|
|
22
22
|
*/
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
const after = this.state[index + 1];
|
|
35
|
-
if (atIndex.length == 0) {
|
|
36
|
-
atIndex.push(new FNode(this.totalOrder.createBetween(before[before.length - 1].position), value));
|
|
37
|
-
}
|
|
38
|
-
else {
|
|
39
|
-
const a = atIndex[atIndex.length - 1];
|
|
40
|
-
atIndex.push(new FNode(this.totalOrder.createBetween(a.position, after[after.length - 1].position), value));
|
|
23
|
+
insertAtPosition(position, value) {
|
|
24
|
+
let index = this.state.length;
|
|
25
|
+
for (let i = 0; i < this.state.length; ++i) {
|
|
26
|
+
const n = this.state[i][0];
|
|
27
|
+
// Compare positions, if the value should be
|
|
28
|
+
// ordered before this one, we found our index
|
|
29
|
+
// this should fix the issue with inserting in the
|
|
30
|
+
// middle messing up order
|
|
31
|
+
if (this.totalOrder.compare(position, n.position) < 0) {
|
|
32
|
+
index = i;
|
|
33
|
+
break;
|
|
41
34
|
}
|
|
42
35
|
}
|
|
36
|
+
console.log({ insertIndex: index });
|
|
37
|
+
if (index >= this.state.length) {
|
|
38
|
+
this.state.push([]);
|
|
39
|
+
}
|
|
43
40
|
else {
|
|
44
|
-
|
|
45
|
-
atIndex.push(new FNode(this.totalOrder.createBetween(), value));
|
|
46
|
-
}
|
|
47
|
-
else {
|
|
48
|
-
const a = atIndex[atIndex.length - 1];
|
|
49
|
-
atIndex.push(new FNode(this.totalOrder.createBetween(a.position), value));
|
|
50
|
-
}
|
|
41
|
+
this.state.splice(index, 0, []);
|
|
51
42
|
}
|
|
52
|
-
//
|
|
43
|
+
// Check if this position already exists at this index
|
|
44
|
+
// If it does, we don't insert again
|
|
45
|
+
const cell = this.state[index];
|
|
46
|
+
const existing = cell.find((n) => this.totalOrder.compare(n.position, position) === 0);
|
|
47
|
+
if (existing)
|
|
48
|
+
return;
|
|
49
|
+
cell.push(new FNode(position, value));
|
|
50
|
+
}
|
|
51
|
+
/**
|
|
52
|
+
* Generates unique position for new element at 'index'
|
|
53
|
+
* @param index - Index to generate position for
|
|
54
|
+
* @returns Generated position
|
|
55
|
+
*/
|
|
56
|
+
generatePosition(index) {
|
|
57
|
+
// If this is the first thing in the document
|
|
58
|
+
if (this.state.length === 0)
|
|
59
|
+
return this.totalOrder.createBetween();
|
|
60
|
+
const prev = index > 0 ? this.findVisiblePosition(index - 1) : undefined;
|
|
61
|
+
const next = this.findVisiblePosition(index);
|
|
62
|
+
return this.totalOrder.createBetween(prev, next);
|
|
63
|
+
}
|
|
64
|
+
/**
|
|
65
|
+
* Inserts new element with 'value' at 'index' in the list
|
|
66
|
+
* @param index - Index to insert 'value' at
|
|
67
|
+
* @param value - Value to insert
|
|
68
|
+
*/
|
|
69
|
+
insert(index, value) {
|
|
70
|
+
const pos = this.generatePosition(index);
|
|
71
|
+
console.log({ index, pos });
|
|
72
|
+
this.insertAtPosition(pos, value);
|
|
53
73
|
this.propagate({
|
|
74
|
+
replicaId: this.totalOrder.getReplicaId(),
|
|
54
75
|
operation: Operation.INSERT,
|
|
55
|
-
position:
|
|
76
|
+
position: pos,
|
|
56
77
|
data: value,
|
|
57
78
|
});
|
|
58
79
|
}
|
|
80
|
+
/**
|
|
81
|
+
* Deletes value at given position
|
|
82
|
+
* @param position - Position to delete at
|
|
83
|
+
*/
|
|
84
|
+
deleteAtPosition(position) {
|
|
85
|
+
// Find the cell containing this position
|
|
86
|
+
for (let i = 0; i < this.state.length; ++i) {
|
|
87
|
+
const cell = this.state[i];
|
|
88
|
+
const node = cell.find((n) => this.totalOrder.compare(n.position, position) === 0);
|
|
89
|
+
if (node) {
|
|
90
|
+
// Tombstone the node, TODO: Implement garbage collection
|
|
91
|
+
node.value = undefined;
|
|
92
|
+
return;
|
|
93
|
+
}
|
|
94
|
+
}
|
|
95
|
+
}
|
|
96
|
+
/**
|
|
97
|
+
* Finds the position of the visible value at index
|
|
98
|
+
* this ignores tombstoned values
|
|
99
|
+
* @param index - Index of the visible value
|
|
100
|
+
*/
|
|
101
|
+
findVisiblePosition(index) {
|
|
102
|
+
let count = 0;
|
|
103
|
+
for (const cell of this.state) {
|
|
104
|
+
for (const n of cell) {
|
|
105
|
+
if (n.value !== undefined) {
|
|
106
|
+
if (count === index)
|
|
107
|
+
return n.position;
|
|
108
|
+
count++;
|
|
109
|
+
}
|
|
110
|
+
}
|
|
111
|
+
}
|
|
112
|
+
}
|
|
113
|
+
/**
|
|
114
|
+
* Finds the visible index of the value at position
|
|
115
|
+
* this ignores tombstoned values
|
|
116
|
+
* @param position - Position to find visible index for
|
|
117
|
+
* @returns
|
|
118
|
+
*/
|
|
119
|
+
findVisibleIndex(position) {
|
|
120
|
+
let count = 0;
|
|
121
|
+
for (const cell of this.state) {
|
|
122
|
+
for (const n of cell) {
|
|
123
|
+
if (n.value !== undefined) {
|
|
124
|
+
if (this.totalOrder.compare(n.position, position) === 0)
|
|
125
|
+
return count;
|
|
126
|
+
count++;
|
|
127
|
+
}
|
|
128
|
+
}
|
|
129
|
+
}
|
|
130
|
+
return undefined;
|
|
131
|
+
}
|
|
59
132
|
/**
|
|
60
133
|
* Delete value in the list at index
|
|
61
134
|
* @param index - Index of the value to delete
|
|
62
135
|
*/
|
|
63
136
|
delete(index) {
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
137
|
+
const position = this.findVisiblePosition(index);
|
|
138
|
+
console.log({ index, position });
|
|
139
|
+
if (!position) {
|
|
140
|
+
console.warn(`No element at position -> ${position}`);
|
|
141
|
+
return;
|
|
68
142
|
}
|
|
69
|
-
this.
|
|
143
|
+
this.deleteAtPosition(position);
|
|
70
144
|
// Send to replicas
|
|
71
145
|
this.propagate({
|
|
146
|
+
replicaId: this.totalOrder.getReplicaId(),
|
|
72
147
|
operation: Operation.DELETE,
|
|
73
|
-
position:
|
|
148
|
+
position: position,
|
|
74
149
|
data: null,
|
|
75
150
|
});
|
|
76
151
|
}
|
|
152
|
+
/**
|
|
153
|
+
* Observes the current visible state of the list
|
|
154
|
+
* @returns The current visible state of the list as a string
|
|
155
|
+
*/
|
|
77
156
|
observe() {
|
|
78
157
|
let res = new String();
|
|
79
158
|
for (const idx of this.state) {
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
res +=
|
|
159
|
+
// Filter out tombstoned nodes and sort by unique position
|
|
160
|
+
const nodes = idx
|
|
161
|
+
.filter((n) => n.value !== undefined)
|
|
162
|
+
.sort((a, b) => this.totalOrder.compare(a.position, b.position));
|
|
163
|
+
// Then append to resultdkjand if somehow
|
|
164
|
+
//a value is undefined append the placeholder thorn
|
|
165
|
+
for (const n of nodes) {
|
|
166
|
+
res += n.value || "Þ";
|
|
88
167
|
}
|
|
89
168
|
}
|
|
90
169
|
return res.toString();
|
|
91
170
|
}
|
|
92
171
|
effect(msg) {
|
|
93
|
-
|
|
94
|
-
|
|
172
|
+
const { replicaId, operation, data, position } = msg;
|
|
173
|
+
if (replicaId == this.totalOrder.getReplicaId())
|
|
174
|
+
return;
|
|
95
175
|
switch (operation) {
|
|
96
|
-
// Operation.INSERT -> insert
|
|
97
176
|
case Operation.INSERT:
|
|
98
177
|
if (!data)
|
|
99
178
|
throw Error("Data is required for Operation.INSERT");
|
|
100
|
-
this.
|
|
101
|
-
// Operation.DELETE -> delete
|
|
179
|
+
return this.insertAtPosition(position, data);
|
|
102
180
|
case Operation.DELETE:
|
|
103
|
-
this.
|
|
181
|
+
return this.deleteAtPosition(position);
|
|
104
182
|
}
|
|
105
183
|
throw Error("Invalid operation");
|
|
106
184
|
}
|
|
185
|
+
replicaId() {
|
|
186
|
+
return this.totalOrder.getReplicaId();
|
|
187
|
+
}
|
|
107
188
|
}
|
|
108
|
-
export default FugueList;
|
|
@@ -1,7 +1,9 @@
|
|
|
1
|
-
import UniquelyDenseTotalOrder from "./UniquelyDenseTotalOrder";
|
|
2
|
-
export
|
|
1
|
+
import { UniquelyDenseTotalOrder } from "./UniquelyDenseTotalOrder";
|
|
2
|
+
export type StringPosition = string;
|
|
3
|
+
export declare class StringTotalOrder implements UniquelyDenseTotalOrder<StringPosition> {
|
|
3
4
|
readonly replicaID: string;
|
|
4
5
|
private counter;
|
|
6
|
+
getReplicaId(): string;
|
|
5
7
|
compare(a: string, b: string): number;
|
|
6
8
|
constructor(replicaID: string);
|
|
7
9
|
createBetween(a?: string, b?: string): string;
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"StringTotalOrder.d.ts","sourceRoot":"","sources":["../../src/TotalOrder/StringTotalOrder.ts"],"names":[],"mappings":"AAAA,OAAO,uBAAuB,MAAM,2BAA2B,CAAC;
|
|
1
|
+
{"version":3,"file":"StringTotalOrder.d.ts","sourceRoot":"","sources":["../../src/TotalOrder/StringTotalOrder.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,uBAAuB,EAAE,MAAM,2BAA2B,CAAC;AAEpE,MAAM,MAAM,cAAc,GAAG,MAAM,CAAC;AAEpC,qBAAa,gBAAiB,YAAW,uBAAuB,CAAC,cAAc,CAAC;IAC5E,QAAQ,CAAC,SAAS,EAAE,MAAM,CAAC;IAC3B,OAAO,CAAC,OAAO,CAAK;IAEpB,YAAY,IAAI,MAAM;IAItB,OAAO,CAAC,CAAC,EAAE,MAAM,EAAE,CAAC,EAAE,MAAM,GAAG,MAAM;gBAIzB,SAAS,EAAE,MAAM;IAI7B,aAAa,CAAC,CAAC,CAAC,EAAE,MAAM,EAAE,CAAC,CAAC,EAAE,MAAM,GAAG,MAAM;CA6BhD"}
|
|
@@ -1,4 +1,7 @@
|
|
|
1
|
-
export
|
|
1
|
+
export class StringTotalOrder {
|
|
2
|
+
getReplicaId() {
|
|
3
|
+
return this.replicaID;
|
|
4
|
+
}
|
|
2
5
|
compare(a, b) {
|
|
3
6
|
return a.localeCompare(b);
|
|
4
7
|
}
|
|
@@ -15,13 +18,13 @@ export default class StringTotalOrder {
|
|
|
15
18
|
}
|
|
16
19
|
// If node is the first position at that index
|
|
17
20
|
if (!a) {
|
|
18
|
-
return b + uniqueStr + "R";
|
|
21
|
+
return b.slice(0, -1) + "L" + uniqueStr + "R";
|
|
19
22
|
}
|
|
20
23
|
// If node is the last position at that index
|
|
21
24
|
if (!b) {
|
|
22
25
|
return a + uniqueStr + "R";
|
|
23
26
|
}
|
|
24
|
-
const isAPrefixOfB = b.
|
|
27
|
+
const isAPrefixOfB = b.startsWith(a);
|
|
25
28
|
// If a is not a prefix of b append a globally unique new string to a and return that +R
|
|
26
29
|
if (!isAPrefixOfB) {
|
|
27
30
|
return a + uniqueStr + "R";
|
|
@@ -4,7 +4,8 @@
|
|
|
4
4
|
*
|
|
5
5
|
* @type P The type of positions. Treated as immutable.
|
|
6
6
|
*/
|
|
7
|
-
export
|
|
7
|
+
export interface UniquelyDenseTotalOrder<P> {
|
|
8
|
+
getReplicaId(): string;
|
|
8
9
|
/**
|
|
9
10
|
* Usual compare function for sorts: returns negative if a < b in
|
|
10
11
|
* their sort order, positive if a > b.
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"UniquelyDenseTotalOrder.d.ts","sourceRoot":"","sources":["../../src/TotalOrder/UniquelyDenseTotalOrder.ts"],"names":[],"mappings":"AAAA;;;;;GAKG;AACH,MAAM,
|
|
1
|
+
{"version":3,"file":"UniquelyDenseTotalOrder.d.ts","sourceRoot":"","sources":["../../src/TotalOrder/UniquelyDenseTotalOrder.ts"],"names":[],"mappings":"AAAA;;;;;GAKG;AACH,MAAM,WAAW,uBAAuB,CAAC,CAAC;IACtC,YAAY,IAAI,MAAM,CAAC;IAEvB;;;OAGG;IACH,OAAO,CAAC,CAAC,EAAE,CAAC,EAAE,CAAC,EAAE,CAAC,GAAG,MAAM,CAAC;IAE5B;;;;;;;;;;OAUG;IACH,aAAa,CAAC,CAAC,CAAC,EAAE,CAAC,EAAE,CAAC,CAAC,EAAE,CAAC,GAAG,CAAC,CAAC;CAClC"}
|
package/dist/index.d.ts
CHANGED
|
@@ -1,8 +1,7 @@
|
|
|
1
|
-
|
|
2
|
-
|
|
3
|
-
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
export { FugueList, CausalTree, UniquelyDenseTotalOrder, StringTotalOrder, FugueMessage, FugueState, Operation, Data, };
|
|
1
|
+
export * from "./Fugue/FugueList";
|
|
2
|
+
export * from "./CausalTree/CausalTree";
|
|
3
|
+
export * from "./TotalOrder/UniquelyDenseTotalOrder";
|
|
4
|
+
export * from "./TotalOrder/StringTotalOrder";
|
|
5
|
+
export * from "./types/Message";
|
|
6
|
+
export * from "./types/Fugue";
|
|
8
7
|
//# sourceMappingURL=index.d.ts.map
|
package/dist/index.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA,
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA,cAAc,mBAAmB,CAAC;AAClC,cAAc,yBAAyB,CAAC;AAExC,cAAc,sCAAsC,CAAC;AACrD,cAAc,+BAA+B,CAAC;AAC9C,cAAc,iBAAiB,CAAC;AAChC,cAAc,eAAe,CAAC"}
|
package/dist/index.js
CHANGED
|
@@ -1,7 +1,6 @@
|
|
|
1
|
-
|
|
2
|
-
|
|
3
|
-
|
|
4
|
-
|
|
5
|
-
export
|
|
6
|
-
|
|
7
|
-
FugueList, CausalTree, StringTotalOrder, Operation, };
|
|
1
|
+
export * from "./Fugue/FugueList";
|
|
2
|
+
export * from "./CausalTree/CausalTree";
|
|
3
|
+
export * from "./TotalOrder/UniquelyDenseTotalOrder";
|
|
4
|
+
export * from "./TotalOrder/StringTotalOrder";
|
|
5
|
+
export * from "./types/Message";
|
|
6
|
+
export * from "./types/Fugue";
|
package/dist/types/Fugue.d.ts
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"Fugue.d.ts","sourceRoot":"","sources":["../../src/types/Fugue.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,MAAM,gBAAgB,CAAC;
|
|
1
|
+
{"version":3,"file":"Fugue.d.ts","sourceRoot":"","sources":["../../src/types/Fugue.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,KAAK,EAAE,MAAM,gBAAgB,CAAC;AAEvC,MAAM,MAAM,UAAU,CAAC,CAAC,IAAI,KAAK,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC"}
|
package/dist/types/Message.d.ts
CHANGED
|
@@ -2,11 +2,11 @@ export declare enum Operation {
|
|
|
2
2
|
INSERT = 0,
|
|
3
3
|
DELETE = 1
|
|
4
4
|
}
|
|
5
|
-
export type Position = number;
|
|
6
5
|
export type Data = string;
|
|
7
|
-
export interface FugueMessage {
|
|
6
|
+
export interface FugueMessage<P> {
|
|
7
|
+
replicaId: string;
|
|
8
8
|
operation: Operation;
|
|
9
|
-
position:
|
|
9
|
+
position: P;
|
|
10
10
|
data: Data | null;
|
|
11
11
|
}
|
|
12
12
|
//# sourceMappingURL=Message.d.ts.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"Message.d.ts","sourceRoot":"","sources":["../../src/types/Message.ts"],"names":[],"mappings":"AAAA,oBAAY,SAAS;IACjB,MAAM,IAAA;IACN,MAAM,IAAA;CACT;
|
|
1
|
+
{"version":3,"file":"Message.d.ts","sourceRoot":"","sources":["../../src/types/Message.ts"],"names":[],"mappings":"AAAA,oBAAY,SAAS;IACjB,MAAM,IAAA;IACN,MAAM,IAAA;CACT;AAGD,MAAM,MAAM,IAAI,GAAG,MAAM,CAAC;AAE1B,MAAM,WAAW,YAAY,CAAC,CAAC;IAC3B,SAAS,EAAE,MAAM,CAAC;IAClB,SAAS,EAAE,SAAS,CAAC;IACrB,QAAQ,EAAE,CAAC,CAAC;IACZ,IAAI,EAAE,IAAI,GAAG,IAAI,CAAC;CACrB"}
|
package/package.json
CHANGED
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"file":"FugueTest.d.ts","sourceRoot":"","sources":["../../src/Fugue/FugueTest.ts"],"names":[],"mappings":""}
|
package/dist/Fugue/FugueTest.js
DELETED
|
@@ -1,22 +0,0 @@
|
|
|
1
|
-
import FugueList from "./FugueList";
|
|
2
|
-
import { randomString } from "../utils/index";
|
|
3
|
-
import StringTotalOrder from "../TotalOrder/StringTotalOrder";
|
|
4
|
-
const test1 = new FugueList(new StringTotalOrder(randomString(5)));
|
|
5
|
-
const test2 = new FugueList(new StringTotalOrder(randomString(5)));
|
|
6
|
-
const word1 = "SHADOW WIZARD MONEY GANG";
|
|
7
|
-
const word2 = "BALLING";
|
|
8
|
-
// Randomly insert words into the list from different simulated users
|
|
9
|
-
async function simulateUser(list, word) {
|
|
10
|
-
for (let i = 0; i < word.length; i++) {
|
|
11
|
-
await new Promise((resolve) => setTimeout(resolve, Math.random() * 150));
|
|
12
|
-
list.insert(i, word.charAt(i));
|
|
13
|
-
console.log(list.observe());
|
|
14
|
-
console.log(list.state);
|
|
15
|
-
}
|
|
16
|
-
}
|
|
17
|
-
Promise.all([
|
|
18
|
-
simulateUser(test1, word1), //
|
|
19
|
-
simulateUser(test2, word2),
|
|
20
|
-
])
|
|
21
|
-
.then(() => console.log(`Done:\t${test1.observe()}\n\t${test2.observe()}`))
|
|
22
|
-
.catch(console.error);
|
package/dist/Fugue/utils.d.ts
DELETED
|
@@ -1,34 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* Helper interface for sorting and creating unique immutable positions,
|
|
3
|
-
* suitable for use in a List CRDT. Taken from mattweidner.com/2022/10/21/basic-list-crdt.html
|
|
4
|
-
*
|
|
5
|
-
* @type P The type of positions. Treated as immutable.
|
|
6
|
-
*/
|
|
7
|
-
export interface UniquelyDenseTotalOrder<P> {
|
|
8
|
-
/**
|
|
9
|
-
* Usual compare function for sorts: returns negative if a < b in
|
|
10
|
-
* their sort order, positive if a > b.
|
|
11
|
-
*/
|
|
12
|
-
compare(a: P, b: P): number;
|
|
13
|
-
/**
|
|
14
|
-
* Returns a globally unique new position c such that a < c < b.
|
|
15
|
-
*
|
|
16
|
-
* "Globally unique" means that the created position must be distinct
|
|
17
|
-
* from all other created positions, including ones created concurrently
|
|
18
|
-
* by other users.
|
|
19
|
-
*
|
|
20
|
-
* When a is undefined, it is treated as the start of the list, i.e.,
|
|
21
|
-
* this returns c such that c < b. Likewise, undefined b is treated
|
|
22
|
-
* as the end of the list.
|
|
23
|
-
*/
|
|
24
|
-
createBetween(a?: P, b?: P): P;
|
|
25
|
-
}
|
|
26
|
-
export declare class StringTotalOrder implements UniquelyDenseTotalOrder<string> {
|
|
27
|
-
readonly replicaID: string;
|
|
28
|
-
private counter;
|
|
29
|
-
compare(a: string, b: string): number;
|
|
30
|
-
constructor(replicaID: string);
|
|
31
|
-
createBetween(a?: string, b?: string): string;
|
|
32
|
-
}
|
|
33
|
-
export declare function randomString(length?: number): string;
|
|
34
|
-
//# sourceMappingURL=utils.d.ts.map
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"file":"utils.d.ts","sourceRoot":"","sources":["../../src/Fugue/utils.ts"],"names":[],"mappings":"AAAA;;;;;GAKG;AACH,MAAM,WAAW,uBAAuB,CAAC,CAAC;IACtC;;;OAGG;IACH,OAAO,CAAC,CAAC,EAAE,CAAC,EAAE,CAAC,EAAE,CAAC,GAAG,MAAM,CAAC;IAE5B;;;;;;;;;;OAUG;IACH,aAAa,CAAC,CAAC,CAAC,EAAE,CAAC,EAAE,CAAC,CAAC,EAAE,CAAC,GAAG,CAAC,CAAC;CAClC;AAED,qBAAa,gBAAiB,YAAW,uBAAuB,CAAC,MAAM,CAAC;IACpE,QAAQ,CAAC,SAAS,EAAE,MAAM,CAAC;IAC3B,OAAO,CAAC,OAAO,CAAK;IAEpB,OAAO,CAAC,CAAC,EAAE,MAAM,EAAE,CAAC,EAAE,MAAM,GAAG,MAAM;gBAIzB,SAAS,EAAE,MAAM;IAI7B,aAAa,CAAC,CAAC,CAAC,EAAE,MAAM,EAAE,CAAC,CAAC,EAAE,MAAM,GAAG,MAAM;CA6BhD;AAED,wBAAgB,YAAY,CAAC,MAAM,GAAE,MAAW,GAAG,MAAM,CAIxD"}
|
package/dist/Fugue/utils.js
DELETED
|
@@ -1,41 +0,0 @@
|
|
|
1
|
-
export class StringTotalOrder {
|
|
2
|
-
compare(a, b) {
|
|
3
|
-
return a.localeCompare(b);
|
|
4
|
-
}
|
|
5
|
-
constructor(replicaID) {
|
|
6
|
-
this.counter = 0;
|
|
7
|
-
this.replicaID = replicaID;
|
|
8
|
-
}
|
|
9
|
-
createBetween(a, b) {
|
|
10
|
-
// Create a wholly unique string using a causal dot, i.e. (replicaID, counter)
|
|
11
|
-
const uniqueStr = `${this.replicaID}${this.counter++}`;
|
|
12
|
-
// If node is the first ever position in the document
|
|
13
|
-
if (!a && !b) {
|
|
14
|
-
return uniqueStr + "R";
|
|
15
|
-
}
|
|
16
|
-
// If node is the first position at that index
|
|
17
|
-
if (!a) {
|
|
18
|
-
return b + uniqueStr + "R";
|
|
19
|
-
}
|
|
20
|
-
// If node is the last position at that index
|
|
21
|
-
if (!b) {
|
|
22
|
-
return a + uniqueStr + "R";
|
|
23
|
-
}
|
|
24
|
-
const isAPrefixOfB = b.substring(0, a.length).localeCompare(a);
|
|
25
|
-
// If a is not a prefix of b append a globally unique new string to a and return that +R
|
|
26
|
-
if (!isAPrefixOfB) {
|
|
27
|
-
return a + uniqueStr + "R";
|
|
28
|
-
}
|
|
29
|
-
else {
|
|
30
|
-
// If a is a prefix of b replace the R at the end of b with L.
|
|
31
|
-
// Then append a globally unique string to it and return it +R.
|
|
32
|
-
return b.slice(0, -1) + "L" + uniqueStr + "R";
|
|
33
|
-
}
|
|
34
|
-
}
|
|
35
|
-
}
|
|
36
|
-
export function randomString(length = 10) {
|
|
37
|
-
let res = new Array(length);
|
|
38
|
-
for (let i = 0; i < length; i++)
|
|
39
|
-
res[i] = String.fromCharCode(97 + Math.floor(Math.random() * 26));
|
|
40
|
-
return res.join("");
|
|
41
|
-
}
|