@cr_docs_t/dts 0.0.12 → 0.2.1
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.
|
@@ -9,8 +9,15 @@ export declare class FugueList<P> {
|
|
|
9
9
|
totalOrder: UniquelyDenseTotalOrder<P>;
|
|
10
10
|
positionCounter: number;
|
|
11
11
|
ws: WebSocket | null;
|
|
12
|
-
|
|
12
|
+
documentID: string;
|
|
13
|
+
readonly batchSize = 100;
|
|
14
|
+
constructor(totalOrder: UniquelyDenseTotalOrder<P>, ws: WebSocket | null, documentID: string);
|
|
15
|
+
/**
|
|
16
|
+
* Propagates message or messages to replicas
|
|
17
|
+
* @param msg - Message or messages to propagate to replicas
|
|
18
|
+
*/
|
|
13
19
|
private propagate;
|
|
20
|
+
private binarySearchPosition;
|
|
14
21
|
/**
|
|
15
22
|
* Inserts a value at a given position
|
|
16
23
|
* @param position - Position to insert at
|
|
@@ -29,6 +36,13 @@ export declare class FugueList<P> {
|
|
|
29
36
|
* @param value - Value to insert
|
|
30
37
|
*/
|
|
31
38
|
insert(index: number, value: string): void;
|
|
39
|
+
/**
|
|
40
|
+
* Inserts multiple characters at given index, this
|
|
41
|
+
* handles large insertions by batching messages
|
|
42
|
+
* @param index - Index to insert at
|
|
43
|
+
* @param value - Value to insert
|
|
44
|
+
*/
|
|
45
|
+
insertMultiple(index: number, value: string): void;
|
|
32
46
|
/**
|
|
33
47
|
* Deletes value at given position
|
|
34
48
|
* @param position - Position to delete at
|
|
@@ -52,12 +66,37 @@ export declare class FugueList<P> {
|
|
|
52
66
|
* @param index - Index of the value to delete
|
|
53
67
|
*/
|
|
54
68
|
delete(index: number): void;
|
|
69
|
+
/**
|
|
70
|
+
* Deletes multiple values starting from index, this
|
|
71
|
+
* handles large deletions by batching messages
|
|
72
|
+
* @param index - Starting index to delete from
|
|
73
|
+
* @param count - Number of values to delete
|
|
74
|
+
*/
|
|
75
|
+
deleteMultiple(index: number, count: number): void;
|
|
55
76
|
/**
|
|
56
77
|
* Observes the current visible state of the list
|
|
57
78
|
* @returns The current visible state of the list as a string
|
|
58
79
|
*/
|
|
59
80
|
observe(): string;
|
|
60
|
-
|
|
81
|
+
/**
|
|
82
|
+
* Applies a single effect message to the list
|
|
83
|
+
* @param msg - Message to apply effect for
|
|
84
|
+
*/
|
|
85
|
+
private singleEffect;
|
|
86
|
+
/**
|
|
87
|
+
* Applies batched effect messages to the list
|
|
88
|
+
* @param msgs - Messages to apply effect for in batch
|
|
89
|
+
*/
|
|
90
|
+
private batchEffect;
|
|
91
|
+
/**
|
|
92
|
+
* Applies effect messages to the list
|
|
93
|
+
* @param msg - Message or messages to apply effect for, can be batched
|
|
94
|
+
*/
|
|
95
|
+
effect(msg: FugueMessage<P> | FugueMessage<P>[]): void;
|
|
61
96
|
replicaId(): string;
|
|
97
|
+
/**
|
|
98
|
+
* Performs garbage collection by removing tombstoned nodes from the state
|
|
99
|
+
*/
|
|
100
|
+
garbageCollect(): void;
|
|
62
101
|
}
|
|
63
102
|
//# 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,mBAAmB,CAAC;AAC/C,OAAO,EAAE,uBAAuB,EAAE,MAAM,0CAA0C,CAAC;AACnF,OAAO,EAAE,YAAY,EAAa,MAAM,qBAAqB,CAAC;AAE9D;;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;
|
|
1
|
+
{"version":3,"file":"FugueList.d.ts","sourceRoot":"","sources":["../../src/Fugue/FugueList.ts"],"names":[],"mappings":"AACA,OAAO,EAAE,UAAU,EAAE,MAAM,mBAAmB,CAAC;AAC/C,OAAO,EAAE,uBAAuB,EAAE,MAAM,0CAA0C,CAAC;AACnF,OAAO,EAAE,YAAY,EAAa,MAAM,qBAAqB,CAAC;AAE9D;;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;IACrB,UAAU,EAAE,MAAM,CAAC;IACnB,QAAQ,CAAC,SAAS,OAAO;gBAEb,UAAU,EAAE,uBAAuB,CAAC,CAAC,CAAC,EAAE,EAAE,EAAE,SAAS,GAAG,IAAI,EAAE,UAAU,EAAE,MAAM;IAM5F;;;OAGG;IACH,OAAO,CAAC,SAAS;IAMjB,OAAO,CAAC,oBAAoB;IAmB5B;;;;OAIG;IACH,OAAO,CAAC,gBAAgB;IAqBxB;;;;OAIG;IACH,OAAO,CAAC,gBAAgB;IASxB;;;;OAIG;IACH,MAAM,CAAC,KAAK,EAAE,MAAM,EAAE,KAAK,EAAE,MAAM;IAenC;;;;;OAKG;IACH,cAAc,CAAC,KAAK,EAAE,MAAM,EAAE,KAAK,EAAE,MAAM;IAgD3C;;;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;IAqBpB;;;;;OAKG;IACH,cAAc,CAAC,KAAK,EAAE,MAAM,EAAE,KAAK,EAAE,MAAM;IAoD3C;;;OAGG;IACH,OAAO,IAAI,MAAM;IAmBjB;;;OAGG;IACH,OAAO,CAAC,YAAY;IAcpB;;;OAGG;IACH,OAAO,CAAC,WAAW;IAqGnB;;;OAGG;IACH,MAAM,CAAC,GAAG,EAAE,YAAY,CAAC,CAAC,CAAC,GAAG,YAAY,CAAC,CAAC,CAAC,EAAE;IAW/C,SAAS,IAAI,MAAM;IAInB;;OAEG;IACH,cAAc;CAOjB"}
|
package/dist/Fugue/FugueList.js
CHANGED
|
@@ -4,49 +4,65 @@ import { Operation } from "../types/Message.js";
|
|
|
4
4
|
* A Fugue List CRDT, with insert and delete operations
|
|
5
5
|
*/
|
|
6
6
|
export class FugueList {
|
|
7
|
-
constructor(totalOrder, ws) {
|
|
7
|
+
constructor(totalOrder, ws, documentID) {
|
|
8
8
|
this.state = [];
|
|
9
9
|
this.positionCounter = 0;
|
|
10
|
+
this.batchSize = 100;
|
|
10
11
|
this.totalOrder = totalOrder;
|
|
11
12
|
this.ws = ws;
|
|
13
|
+
this.documentID = documentID;
|
|
12
14
|
}
|
|
15
|
+
/**
|
|
16
|
+
* Propagates message or messages to replicas
|
|
17
|
+
* @param msg - Message or messages to propagate to replicas
|
|
18
|
+
*/
|
|
13
19
|
propagate(msg) {
|
|
14
20
|
if (!this.ws)
|
|
15
21
|
return;
|
|
16
22
|
this.ws.send(JSON.stringify(msg));
|
|
17
23
|
}
|
|
24
|
+
binarySearchPosition(position) {
|
|
25
|
+
let low = 0;
|
|
26
|
+
let high = this.state.length - 1;
|
|
27
|
+
while (low <= high) {
|
|
28
|
+
const mid = Math.floor((low + high) / 2);
|
|
29
|
+
const midPos = this.state[mid][0].position;
|
|
30
|
+
const cmp = this.totalOrder.compare(midPos, position);
|
|
31
|
+
if (cmp === 0) {
|
|
32
|
+
return mid;
|
|
33
|
+
}
|
|
34
|
+
else if (cmp < 0) {
|
|
35
|
+
low = mid + 1;
|
|
36
|
+
}
|
|
37
|
+
else {
|
|
38
|
+
high = mid - 1;
|
|
39
|
+
}
|
|
40
|
+
}
|
|
41
|
+
// If not found, return the position where it can be inserted
|
|
42
|
+
return low;
|
|
43
|
+
}
|
|
18
44
|
/**
|
|
19
45
|
* Inserts a value at a given position
|
|
20
46
|
* @param position - Position to insert at
|
|
21
47
|
* @param value - Value to insert
|
|
22
48
|
*/
|
|
23
49
|
insertAtPosition(position, value) {
|
|
24
|
-
let index = this.
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
//
|
|
30
|
-
//
|
|
31
|
-
if (
|
|
32
|
-
|
|
33
|
-
|
|
50
|
+
let index = this.binarySearchPosition(position);
|
|
51
|
+
// Check if the position already exists at this index, i.e. there is a collision
|
|
52
|
+
if (index < this.state.length && this.totalOrder.compare(this.state[index][0].position, position) === 0) {
|
|
53
|
+
const cell = this.state[index];
|
|
54
|
+
const existing = cell.find((n) => this.totalOrder.compare(n.position, position) === 0);
|
|
55
|
+
// Don't insert if it already exists,
|
|
56
|
+
// TODO: ideally this should trigger a collision resolution
|
|
57
|
+
if (!existing) {
|
|
58
|
+
cell.push(new FNode(position, value));
|
|
59
|
+
cell.sort((a, b) => this.totalOrder.compare(a.position, b.position));
|
|
34
60
|
}
|
|
35
61
|
}
|
|
36
|
-
|
|
37
|
-
if (index >= this.state.length) {
|
|
38
|
-
this.state.push([]);
|
|
39
|
-
}
|
|
62
|
+
// Insert new cell at index
|
|
40
63
|
else {
|
|
41
|
-
this.state.splice(index, 0, []);
|
|
64
|
+
this.state.splice(index, 0, [new FNode(position, value)]);
|
|
42
65
|
}
|
|
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
66
|
}
|
|
51
67
|
/**
|
|
52
68
|
* Generates unique position for new element at 'index'
|
|
@@ -71,12 +87,59 @@ export class FugueList {
|
|
|
71
87
|
console.log({ index, pos });
|
|
72
88
|
this.insertAtPosition(pos, value);
|
|
73
89
|
this.propagate({
|
|
90
|
+
documentID: this.documentID,
|
|
74
91
|
replicaId: this.totalOrder.getReplicaId(),
|
|
75
92
|
operation: Operation.INSERT,
|
|
76
93
|
position: pos,
|
|
77
94
|
data: value,
|
|
78
95
|
});
|
|
79
96
|
}
|
|
97
|
+
/**
|
|
98
|
+
* Inserts multiple characters at given index, this
|
|
99
|
+
* handles large insertions by batching messages
|
|
100
|
+
* @param index - Index to insert at
|
|
101
|
+
* @param value - Value to insert
|
|
102
|
+
*/
|
|
103
|
+
insertMultiple(index, value) {
|
|
104
|
+
if (value.length == 0)
|
|
105
|
+
return;
|
|
106
|
+
if (value.length == 1) {
|
|
107
|
+
this.insert(index, value);
|
|
108
|
+
return;
|
|
109
|
+
}
|
|
110
|
+
// Find left and right anchors
|
|
111
|
+
const lA = index > 0 ? this.findVisiblePosition(index - 1) : undefined;
|
|
112
|
+
const rA = this.findVisiblePosition(index);
|
|
113
|
+
const newCells = [];
|
|
114
|
+
let cL = lA;
|
|
115
|
+
let msgs = [];
|
|
116
|
+
for (const c of value) {
|
|
117
|
+
const pos = this.totalOrder.createBetween(cL, rA);
|
|
118
|
+
// Collect new cells
|
|
119
|
+
newCells.push([new FNode(pos, c)]);
|
|
120
|
+
// Batch propagate
|
|
121
|
+
msgs.push({
|
|
122
|
+
documentID: this.documentID,
|
|
123
|
+
replicaId: this.totalOrder.getReplicaId(),
|
|
124
|
+
operation: Operation.INSERT,
|
|
125
|
+
position: pos,
|
|
126
|
+
data: c,
|
|
127
|
+
});
|
|
128
|
+
cL = pos;
|
|
129
|
+
if (msgs.length >= this.batchSize) {
|
|
130
|
+
this.propagate(msgs);
|
|
131
|
+
msgs = [];
|
|
132
|
+
}
|
|
133
|
+
}
|
|
134
|
+
// Single splice to insert all new cells
|
|
135
|
+
const firstPos = newCells[0][0].position;
|
|
136
|
+
const insertIndex = this.binarySearchPosition(firstPos);
|
|
137
|
+
this.state.splice(insertIndex, 0, ...newCells);
|
|
138
|
+
// Propagate remaining
|
|
139
|
+
if (msgs.length > 0) {
|
|
140
|
+
this.propagate(msgs);
|
|
141
|
+
}
|
|
142
|
+
}
|
|
80
143
|
/**
|
|
81
144
|
* Deletes value at given position
|
|
82
145
|
* @param position - Position to delete at
|
|
@@ -143,12 +206,64 @@ export class FugueList {
|
|
|
143
206
|
this.deleteAtPosition(position);
|
|
144
207
|
// Send to replicas
|
|
145
208
|
this.propagate({
|
|
209
|
+
documentID: this.documentID,
|
|
146
210
|
replicaId: this.totalOrder.getReplicaId(),
|
|
147
211
|
operation: Operation.DELETE,
|
|
148
212
|
position: position,
|
|
149
213
|
data: null,
|
|
150
214
|
});
|
|
151
215
|
}
|
|
216
|
+
/**
|
|
217
|
+
* Deletes multiple values starting from index, this
|
|
218
|
+
* handles large deletions by batching messages
|
|
219
|
+
* @param index - Starting index to delete from
|
|
220
|
+
* @param count - Number of values to delete
|
|
221
|
+
*/
|
|
222
|
+
deleteMultiple(index, count) {
|
|
223
|
+
if (count <= 0)
|
|
224
|
+
return;
|
|
225
|
+
if (count == 1) {
|
|
226
|
+
this.delete(index);
|
|
227
|
+
return;
|
|
228
|
+
}
|
|
229
|
+
let currentVisibleIndex = 0;
|
|
230
|
+
let deletedCount = 0;
|
|
231
|
+
let msgs = [];
|
|
232
|
+
outer: for (const c of this.state) {
|
|
233
|
+
for (const n of c) {
|
|
234
|
+
// Only consider visible nodes
|
|
235
|
+
if (n.value !== undefined) {
|
|
236
|
+
if (currentVisibleIndex >= index) {
|
|
237
|
+
const pos = n.position;
|
|
238
|
+
// Tombstone the node
|
|
239
|
+
n.value = undefined;
|
|
240
|
+
deletedCount++;
|
|
241
|
+
// Batch
|
|
242
|
+
msgs.push({
|
|
243
|
+
documentID: this.documentID,
|
|
244
|
+
replicaId: this.totalOrder.getReplicaId(),
|
|
245
|
+
operation: Operation.DELETE,
|
|
246
|
+
position: pos,
|
|
247
|
+
data: null,
|
|
248
|
+
});
|
|
249
|
+
if (msgs.length >= this.batchSize) {
|
|
250
|
+
this.propagate(msgs);
|
|
251
|
+
msgs = [];
|
|
252
|
+
}
|
|
253
|
+
}
|
|
254
|
+
// Processed a visible node
|
|
255
|
+
currentVisibleIndex++;
|
|
256
|
+
// Check if we've deleted enough
|
|
257
|
+
if (deletedCount >= count) {
|
|
258
|
+
break outer;
|
|
259
|
+
}
|
|
260
|
+
}
|
|
261
|
+
}
|
|
262
|
+
}
|
|
263
|
+
if (msgs.length > 0) {
|
|
264
|
+
this.propagate(msgs);
|
|
265
|
+
}
|
|
266
|
+
}
|
|
152
267
|
/**
|
|
153
268
|
* Observes the current visible state of the list
|
|
154
269
|
* @returns The current visible state of the list as a string
|
|
@@ -168,7 +283,11 @@ export class FugueList {
|
|
|
168
283
|
}
|
|
169
284
|
return res.toString();
|
|
170
285
|
}
|
|
171
|
-
|
|
286
|
+
/**
|
|
287
|
+
* Applies a single effect message to the list
|
|
288
|
+
* @param msg - Message to apply effect for
|
|
289
|
+
*/
|
|
290
|
+
singleEffect(msg) {
|
|
172
291
|
const { replicaId, operation, data, position } = msg;
|
|
173
292
|
if (replicaId == this.totalOrder.getReplicaId())
|
|
174
293
|
return;
|
|
@@ -182,7 +301,125 @@ export class FugueList {
|
|
|
182
301
|
}
|
|
183
302
|
throw Error("Invalid operation");
|
|
184
303
|
}
|
|
304
|
+
/**
|
|
305
|
+
* Applies batched effect messages to the list
|
|
306
|
+
* @param msgs - Messages to apply effect for in batch
|
|
307
|
+
*/
|
|
308
|
+
batchEffect(msgs) {
|
|
309
|
+
const inserts = [];
|
|
310
|
+
const deletes = new Set();
|
|
311
|
+
// Separate operations
|
|
312
|
+
for (const msg of msgs) {
|
|
313
|
+
const { replicaId, operation, position, data } = msg;
|
|
314
|
+
if (replicaId == this.totalOrder.getReplicaId())
|
|
315
|
+
continue;
|
|
316
|
+
switch (operation) {
|
|
317
|
+
case Operation.INSERT:
|
|
318
|
+
if (!data)
|
|
319
|
+
continue;
|
|
320
|
+
inserts.push(msg);
|
|
321
|
+
break;
|
|
322
|
+
case Operation.DELETE:
|
|
323
|
+
deletes.add(JSON.stringify(position));
|
|
324
|
+
break;
|
|
325
|
+
}
|
|
326
|
+
}
|
|
327
|
+
// Apply deletes first
|
|
328
|
+
if (deletes.size > 0) {
|
|
329
|
+
for (const posStr of deletes) {
|
|
330
|
+
// Parse the position back from the set
|
|
331
|
+
const pos = JSON.parse(posStr);
|
|
332
|
+
const idx = this.binarySearchPosition(pos);
|
|
333
|
+
// Check if we found the right cell
|
|
334
|
+
if (idx < this.state.length) {
|
|
335
|
+
const cell = this.state[idx];
|
|
336
|
+
// Find the specific node in the collision cell
|
|
337
|
+
const node = cell.find((n) => this.totalOrder.compare(n.position, pos) === 0);
|
|
338
|
+
if (node && node.value !== undefined) {
|
|
339
|
+
node.value = undefined; // Tombstone
|
|
340
|
+
}
|
|
341
|
+
}
|
|
342
|
+
}
|
|
343
|
+
}
|
|
344
|
+
// Then apply inserts
|
|
345
|
+
if (inserts.length > 0) {
|
|
346
|
+
inserts.sort((a, b) => this.totalOrder.compare(a.position, b.position));
|
|
347
|
+
// Group into chunks, of contiguous inserts
|
|
348
|
+
let batchCells = [];
|
|
349
|
+
let startIdx = -1;
|
|
350
|
+
for (const msg of inserts) {
|
|
351
|
+
const { position, data } = msg;
|
|
352
|
+
// Find the index to insert at
|
|
353
|
+
const idx = this.binarySearchPosition(position);
|
|
354
|
+
// Check for collision
|
|
355
|
+
//TODO: should trigger collision resolution
|
|
356
|
+
if (idx < this.state.length && this.totalOrder.compare(this.state[idx][0].position, position) === 0) {
|
|
357
|
+
const cell = this.state[idx];
|
|
358
|
+
const existing = cell.find((n) => this.totalOrder.compare(n.position, position) === 0);
|
|
359
|
+
// Don't insert if it already exists
|
|
360
|
+
if (!existing) {
|
|
361
|
+
cell.push(new FNode(position, data ? data : undefined));
|
|
362
|
+
cell.sort((a, b) => this.totalOrder.compare(a.position, b.position));
|
|
363
|
+
}
|
|
364
|
+
}
|
|
365
|
+
else {
|
|
366
|
+
// Start a new batch if:
|
|
367
|
+
// - It's the first item
|
|
368
|
+
// - This index is not contiguous with the previous group
|
|
369
|
+
if (startIdx === -1) {
|
|
370
|
+
startIdx = idx;
|
|
371
|
+
batchCells = [[new FNode(position, data ? data : undefined)]];
|
|
372
|
+
}
|
|
373
|
+
// If the index is the same as startIdx, continue the batch
|
|
374
|
+
else if (idx === startIdx) {
|
|
375
|
+
batchCells.push([new FNode(position, data ? data : undefined)]);
|
|
376
|
+
}
|
|
377
|
+
// The index is different, i.e. not contiguous, so flush the current batch,
|
|
378
|
+
// commit it and start a new one
|
|
379
|
+
else {
|
|
380
|
+
// Commit batch
|
|
381
|
+
this.state.splice(startIdx, 0, ...batchCells);
|
|
382
|
+
// Start new batch
|
|
383
|
+
// Calculate the shift caused by the splice
|
|
384
|
+
// If the new idx (calculated on old state) is after the splice point,
|
|
385
|
+
// we must add the length of the batch we just inserted.
|
|
386
|
+
const shift = idx >= startIdx ? batchCells.length : 0;
|
|
387
|
+
startIdx = idx + shift;
|
|
388
|
+
batchCells = [[new FNode(position, data ? data : undefined)]];
|
|
389
|
+
}
|
|
390
|
+
}
|
|
391
|
+
}
|
|
392
|
+
// Commit any remaining batch
|
|
393
|
+
if (batchCells.length > 0)
|
|
394
|
+
this.state.splice(startIdx, 0, ...batchCells);
|
|
395
|
+
}
|
|
396
|
+
}
|
|
397
|
+
/**
|
|
398
|
+
* Applies effect messages to the list
|
|
399
|
+
* @param msg - Message or messages to apply effect for, can be batched
|
|
400
|
+
*/
|
|
401
|
+
effect(msg) {
|
|
402
|
+
if (Array.isArray(msg)) {
|
|
403
|
+
this.batchEffect(msg);
|
|
404
|
+
// for (const m of msg) {
|
|
405
|
+
// this.singleEffect(m);
|
|
406
|
+
// }
|
|
407
|
+
}
|
|
408
|
+
else {
|
|
409
|
+
this.singleEffect(msg);
|
|
410
|
+
}
|
|
411
|
+
}
|
|
185
412
|
replicaId() {
|
|
186
413
|
return this.totalOrder.getReplicaId();
|
|
187
414
|
}
|
|
415
|
+
/**
|
|
416
|
+
* Performs garbage collection by removing tombstoned nodes from the state
|
|
417
|
+
*/
|
|
418
|
+
garbageCollect() {
|
|
419
|
+
this.state = this.state.filter((cell) => {
|
|
420
|
+
// Check if cell has any visible nodes
|
|
421
|
+
const hasVisible = cell.some((n) => n.value !== undefined);
|
|
422
|
+
return hasVisible;
|
|
423
|
+
});
|
|
424
|
+
}
|
|
188
425
|
}
|
package/dist/types/Message.d.ts
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"Message.d.ts","sourceRoot":"","sources":["../../src/types/Message.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,UAAU,EAAE,MAAM,YAAY,CAAC;AAExC,oBAAY,SAAS;IACjB,MAAM,IAAA;IACN,MAAM,IAAA;IACN,IAAI,IAAA;CACP;AAED,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;AAED,MAAM,WAAW,gBAAgB,CAAC,CAAC;IAC/B,KAAK,EAAE,UAAU,CAAC,CAAC,CAAC,CAAC;CACxB"}
|
|
1
|
+
{"version":3,"file":"Message.d.ts","sourceRoot":"","sources":["../../src/types/Message.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,UAAU,EAAE,MAAM,YAAY,CAAC;AAExC,oBAAY,SAAS;IACjB,MAAM,IAAA;IACN,MAAM,IAAA;IACN,IAAI,IAAA;CACP;AAED,MAAM,MAAM,IAAI,GAAG,MAAM,CAAC;AAE1B,MAAM,WAAW,YAAY,CAAC,CAAC;IAC3B,UAAU,EAAE,MAAM,CAAC;IACnB,SAAS,EAAE,MAAM,CAAC;IAClB,SAAS,EAAE,SAAS,CAAC;IACrB,QAAQ,EAAE,CAAC,CAAC;IACZ,IAAI,EAAE,IAAI,GAAG,IAAI,CAAC;CACrB;AAED,MAAM,WAAW,gBAAgB,CAAC,CAAC;IAC/B,KAAK,EAAE,UAAU,CAAC,CAAC,CAAC,CAAC;CACxB"}
|
package/package.json
CHANGED
|
@@ -1,12 +1,14 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@cr_docs_t/dts",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.2.1",
|
|
4
4
|
"description": "",
|
|
5
5
|
"main": "dist/index.js",
|
|
6
6
|
"types": "dist/index.d.ts",
|
|
7
7
|
"scripts": {
|
|
8
8
|
"build": "tsc",
|
|
9
|
-
"prepublishOnly": "npm run build"
|
|
9
|
+
"prepublishOnly": "npm run build",
|
|
10
|
+
"ci": "npm install && npm run build",
|
|
11
|
+
"publish": "npm publish --access public"
|
|
10
12
|
},
|
|
11
13
|
"exports": {
|
|
12
14
|
".": {
|
|
@@ -15,6 +17,35 @@
|
|
|
15
17
|
"types": "./dist/index.d.ts"
|
|
16
18
|
}
|
|
17
19
|
},
|
|
20
|
+
"publishConfig": {
|
|
21
|
+
"access": "public"
|
|
22
|
+
},
|
|
23
|
+
"repository": {
|
|
24
|
+
"type": "git",
|
|
25
|
+
"url": "https://github.com/CRDT-Capstone/CRDocsT-dts.git"
|
|
26
|
+
},
|
|
27
|
+
"release": {
|
|
28
|
+
"branches": [
|
|
29
|
+
"master"
|
|
30
|
+
],
|
|
31
|
+
"plugins": [
|
|
32
|
+
"@semantic-release/commit-analyzer",
|
|
33
|
+
"@semantic-release/release-notes-generator",
|
|
34
|
+
"@semantic-release/changelog",
|
|
35
|
+
"@semantic-release/npm",
|
|
36
|
+
"@semantic-release/github",
|
|
37
|
+
[
|
|
38
|
+
"@semantic-release/git",
|
|
39
|
+
{
|
|
40
|
+
"assets": [
|
|
41
|
+
"package.json",
|
|
42
|
+
"CHANGELOG.md"
|
|
43
|
+
],
|
|
44
|
+
"message": "chore(release): ${nextRelease.version} [skip ci]\n\n${nextRelease.notes}"
|
|
45
|
+
}
|
|
46
|
+
]
|
|
47
|
+
]
|
|
48
|
+
},
|
|
18
49
|
"keywords": [],
|
|
19
50
|
"author": [
|
|
20
51
|
"Madiba Hudson-Quansah",
|
|
@@ -23,7 +54,10 @@
|
|
|
23
54
|
"license": "ISC",
|
|
24
55
|
"packageManager": "pnpm@10.20.0",
|
|
25
56
|
"devDependencies": {
|
|
57
|
+
"@semantic-release/changelog": "^6.0.3",
|
|
58
|
+
"@semantic-release/git": "^10.0.1",
|
|
26
59
|
"@types/node": "^24.10.1",
|
|
60
|
+
"semantic-release": "^25.0.2",
|
|
27
61
|
"typescript": "^5.9.3"
|
|
28
62
|
},
|
|
29
63
|
"files": [
|