@vuer-ai/vuer-rtc 0.5.3 → 0.5.4

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.
@@ -1 +1 @@
1
- {"version":3,"file":"coalesceTextOperations.d.ts","sourceRoot":"","sources":["../../src/client/coalesceTextOperations.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;GAWG;AAEH,OAAO,KAAK,EAAE,aAAa,EAAE,MAAM,gBAAgB,CAAC;AACpD,OAAO,KAAK,EAAE,QAAQ,EAAE,MAAM,iBAAiB,CAAC;AAGhD,MAAM,WAAW,eAAe;IAC9B,kEAAkE;IAClE,WAAW,CAAC,EAAE,MAAM,CAAC;CACtB;AAED;;;;;;;;;;GAUG;AACH,wBAAgB,sBAAsB,CACpC,GAAG,EAAE,aAAa,EAAE,EACpB,OAAO,GAAE,eAAoB,GAC5B,aAAa,EAAE,CAuHjB;AAED;;;;;;;;;GASG;AACH,wBAAgB,cAAc,CAAC,CAAC,EAAE,QAAQ,EAAE,CAAC,EAAE,QAAQ,GAAG,MAAM,CAI/D"}
1
+ {"version":3,"file":"coalesceTextOperations.d.ts","sourceRoot":"","sources":["../../src/client/coalesceTextOperations.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;GAWG;AAEH,OAAO,KAAK,EAAE,aAAa,EAAE,MAAM,gBAAgB,CAAC;AACpD,OAAO,KAAK,EAAE,QAAQ,EAAE,MAAM,iBAAiB,CAAC;AAIhD,MAAM,WAAW,eAAe;IAC9B,kEAAkE;IAClE,WAAW,CAAC,EAAE,MAAM,CAAC;CACtB;AAsBD;;;;;;;;;;GAUG;AACH,wBAAgB,sBAAsB,CACpC,GAAG,EAAE,aAAa,EAAE,EACpB,OAAO,GAAE,eAAoB,GAC5B,aAAa,EAAE,CA6HjB;AAED;;;;;;;;;GASG;AACH,wBAAgB,cAAc,CAAC,CAAC,EAAE,QAAQ,EAAE,CAAC,EAAE,QAAQ,GAAG,MAAM,CAI/D"}
@@ -11,6 +11,25 @@
11
11
  * 4. Compatible YATA structure (forms a chain)
12
12
  */
13
13
  import { parseItemId } from '../crdt/Rope.js';
14
+ import { optimizeDeletions, parseItemId as parseId } from './coalescence/utils.js';
15
+ /**
16
+ * Sort and optimize deletions array.
17
+ * Deletions must be sorted in ascending order for optimizeDeletions to work correctly.
18
+ */
19
+ function sortAndOptimizeDeletions(deletions) {
20
+ if (deletions.length === 0)
21
+ return deletions;
22
+ // Sort deletions by agent, then by sequence number (ascending)
23
+ const sorted = [...deletions].sort((a, b) => {
24
+ const aId = parseId(a.id);
25
+ const bId = parseId(b.id);
26
+ if (aId.agent !== bId.agent) {
27
+ return aId.agent.localeCompare(bId.agent);
28
+ }
29
+ return aId.seq - bId.seq;
30
+ });
31
+ return optimizeDeletions(sorted);
32
+ }
14
33
  /**
15
34
  * Coalesce consecutive text insert operations.
16
35
  *
@@ -33,6 +52,8 @@ export function coalesceTextOperations(ops, options = {}) {
33
52
  if (op.type === 'insert') {
34
53
  // Flush any pending delete before starting new insert
35
54
  if (pendingDelete !== null) {
55
+ // Sort and optimize deletions array before flushing
56
+ pendingDelete.op.deletions = sortAndOptimizeDeletions(pendingDelete.op.deletions);
36
57
  result.push(pendingDelete);
37
58
  pendingDelete = null;
38
59
  }
@@ -123,6 +144,8 @@ export function coalesceTextOperations(ops, options = {}) {
123
144
  pendingInsert = null;
124
145
  }
125
146
  if (pendingDelete !== null) {
147
+ // Sort and optimize deletions array before flushing
148
+ pendingDelete.op.deletions = sortAndOptimizeDeletions(pendingDelete.op.deletions);
126
149
  result.push(pendingDelete);
127
150
  pendingDelete = null;
128
151
  }
@@ -134,6 +157,8 @@ export function coalesceTextOperations(ops, options = {}) {
134
157
  result.push(pendingInsert);
135
158
  }
136
159
  if (pendingDelete !== null) {
160
+ // Sort and optimize deletions array before flushing
161
+ pendingDelete.op.deletions = sortAndOptimizeDeletions(pendingDelete.op.deletions);
137
162
  result.push(pendingDelete);
138
163
  }
139
164
  return result;
@@ -1 +1 @@
1
- {"version":3,"file":"coalesceTextOperations.js","sourceRoot":"","sources":["../../src/client/coalesceTextOperations.ts"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"coalesceTextOperations.js","sourceRoot":"","sources":["../../src/client/coalesceTextOperations.ts"],"names":[],"mappings":"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"}
@@ -1 +1 @@
1
- {"version":3,"file":"textDeletes.d.ts","sourceRoot":"","sources":["../../../src/client/coalescence/textDeletes.ts"],"names":[],"mappings":"AAAA;;;;GAIG;AAEH,OAAO,KAAK,EAAE,SAAS,EAAE,MAAM,oCAAoC,CAAC;AAGpE,MAAM,WAAW,YAAY;IAC3B,KAAK,EAAE,aAAa,CAAC;IACrB,GAAG,EAAE,MAAM,CAAC;IACZ,IAAI,EAAE,MAAM,CAAC;IACb,SAAS,EAAE,KAAK,CAAC;QAAE,EAAE,EAAE,MAAM,CAAC;QAAC,MAAM,EAAE,MAAM,CAAA;KAAE,CAAC,CAAC;CAGlD;AAED,MAAM,WAAW,eAAe;IAC9B,kEAAkE;IAClE,WAAW,CAAC,EAAE,MAAM,CAAC;CACtB;AAED;;GAEG;AACH,wBAAgB,cAAc,CAAC,EAAE,EAAE,SAAS,GAAG,EAAE,IAAI,YAAY,CAMhE;AAED;;;;;;;;;;GAUG;AACH,wBAAgB,mBAAmB,CACjC,GAAG,EAAE,YAAY,EAAE,EACnB,OAAO,GAAE,eAAoB,GAC5B,YAAY,EAAE,CAgDhB"}
1
+ {"version":3,"file":"textDeletes.d.ts","sourceRoot":"","sources":["../../../src/client/coalescence/textDeletes.ts"],"names":[],"mappings":"AAAA;;;;GAIG;AAEH,OAAO,KAAK,EAAE,SAAS,EAAE,MAAM,oCAAoC,CAAC;AAGpE,MAAM,WAAW,YAAY;IAC3B,KAAK,EAAE,aAAa,CAAC;IACrB,GAAG,EAAE,MAAM,CAAC;IACZ,IAAI,EAAE,MAAM,CAAC;IACb,SAAS,EAAE,KAAK,CAAC;QAAE,EAAE,EAAE,MAAM,CAAC;QAAC,MAAM,EAAE,MAAM,CAAA;KAAE,CAAC,CAAC;CAGlD;AAED,MAAM,WAAW,eAAe;IAC9B,kEAAkE;IAClE,WAAW,CAAC,EAAE,MAAM,CAAC;CACtB;AAsBD;;GAEG;AACH,wBAAgB,cAAc,CAAC,EAAE,EAAE,SAAS,GAAG,EAAE,IAAI,YAAY,CAMhE;AAED;;;;;;;;;;GAUG;AACH,wBAAgB,mBAAmB,CACjC,GAAG,EAAE,YAAY,EAAE,EACnB,OAAO,GAAE,eAAoB,GAC5B,YAAY,EAAE,CAgDhB"}
@@ -3,7 +3,25 @@
3
3
  *
4
4
  * Merges consecutive text delete operations.
5
5
  */
6
- import { optimizeDeletions } from './utils.js';
6
+ import { optimizeDeletions, parseItemId } from './utils.js';
7
+ /**
8
+ * Sort and optimize deletions array.
9
+ * Deletions must be sorted in ascending order for optimizeDeletions to work correctly.
10
+ */
11
+ function sortAndOptimizeDeletions(deletions) {
12
+ if (deletions.length === 0)
13
+ return deletions;
14
+ // Sort deletions by agent, then by sequence number (ascending)
15
+ const sorted = [...deletions].sort((a, b) => {
16
+ const aId = parseItemId(a.id);
17
+ const bId = parseItemId(b.id);
18
+ if (aId.agent !== bId.agent) {
19
+ return aId.agent.localeCompare(bId.agent);
20
+ }
21
+ return aId.seq - bId.seq;
22
+ });
23
+ return optimizeDeletions(sorted);
24
+ }
7
25
  /**
8
26
  * Check if an operation is a text delete with CRDT metadata
9
27
  */
@@ -58,13 +76,13 @@ export function coalesceTextDeletes(ops, options = {}) {
58
76
  }
59
77
  // Flush any remaining pending delete
60
78
  if (pending !== null) {
61
- // Optimize deletions array before pushing
62
- pending.deletions = optimizeDeletions(pending.deletions);
79
+ // Sort and optimize deletions array before pushing
80
+ pending.deletions = sortAndOptimizeDeletions(pending.deletions);
63
81
  result.push(pending);
64
82
  }
65
- // Also optimize deletions in all previously pushed operations
83
+ // Also sort and optimize deletions in all previously pushed operations
66
84
  for (const op of result) {
67
- op.deletions = optimizeDeletions(op.deletions);
85
+ op.deletions = sortAndOptimizeDeletions(op.deletions);
68
86
  }
69
87
  return result;
70
88
  }
@@ -1 +1 @@
1
- {"version":3,"file":"textDeletes.js","sourceRoot":"","sources":["../../../src/client/coalescence/textDeletes.ts"],"names":[],"mappings":"AAAA;;;;GAIG;AAGH,OAAO,EAAE,iBAAiB,EAAE,MAAM,YAAY,CAAC;AAgB/C;;GAEG;AACH,MAAM,UAAU,cAAc,CAAC,EAAa;IAC1C,OAAO,CACL,EAAE,CAAC,KAAK,KAAK,aAAa;QAC1B,KAAK,CAAC,OAAO,CAAE,EAAU,CAAC,SAAS,CAAC;QACnC,EAAU,CAAC,SAAS,CAAC,MAAM,GAAG,CAAC,CACjC,CAAC;AACJ,CAAC;AAED;;;;;;;;;;GAUG;AACH,MAAM,UAAU,mBAAmB,CACjC,GAAmB,EACnB,UAA2B,EAAE;IAE7B,MAAM,EAAE,WAAW,GAAG,IAAI,EAAE,GAAG,OAAO,CAAC;IAEvC,IAAI,GAAG,CAAC,MAAM,KAAK,CAAC;QAAE,OAAO,GAAG,CAAC;IAEjC,MAAM,MAAM,GAAmB,EAAE,CAAC;IAClC,IAAI,OAAO,GAAwB,IAAI,CAAC;IAExC,KAAK,MAAM,EAAE,IAAI,GAAG,EAAE,CAAC;QACrB,IAAI,OAAO,KAAK,IAAI,EAAE,CAAC;YACrB,2BAA2B;YAC3B,OAAO,GAAG,EAAE,GAAG,EAAE,EAAE,SAAS,EAAE,CAAC,GAAG,EAAE,CAAC,SAAS,CAAC,EAAE,CAAC;QACpD,CAAC;aAAM,CAAC;YACN,yBAAyB;YACzB,MAAM,UAAU,GAAG,OAAO,CAAC,GAAG,KAAK,EAAE,CAAC,GAAG,IAAI,OAAO,CAAC,IAAI,KAAK,EAAE,CAAC,IAAI,CAAC;YAEtE,uEAAuE;YACvE,6CAA6C;YAC7C,IAAI,UAAU,EAAE,CAAC;gBACf,4CAA4C;gBAC5C,MAAM,MAAM,GAAiB;oBAC3B,KAAK,EAAE,aAAa;oBACpB,GAAG,EAAE,OAAO,CAAC,GAAG;oBAChB,IAAI,EAAE,OAAO,CAAC,IAAI;oBAClB,SAAS,EAAE,CAAC,GAAG,OAAO,CAAC,SAAS,EAAE,GAAG,EAAE,CAAC,SAAS,CAAC;iBACnD,CAAC;gBACF,OAAO,GAAG,MAAM,CAAC;YACnB,CAAC;iBAAM,CAAC;gBACN,4CAA4C;gBAC5C,MAAM,CAAC,IAAI,CAAC,OAAO,CAAC,CAAC;gBACrB,OAAO,GAAG,EAAE,GAAG,EAAE,EAAE,SAAS,EAAE,CAAC,GAAG,EAAE,CAAC,SAAS,CAAC,EAAE,CAAC;YACpD,CAAC;QACH,CAAC;IACH,CAAC;IAED,qCAAqC;IACrC,IAAI,OAAO,KAAK,IAAI,EAAE,CAAC;QACrB,0CAA0C;QAC1C,OAAO,CAAC,SAAS,GAAG,iBAAiB,CAAC,OAAO,CAAC,SAAS,CAAC,CAAC;QACzD,MAAM,CAAC,IAAI,CAAC,OAAO,CAAC,CAAC;IACvB,CAAC;IAED,8DAA8D;IAC9D,KAAK,MAAM,EAAE,IAAI,MAAM,EAAE,CAAC;QACxB,EAAE,CAAC,SAAS,GAAG,iBAAiB,CAAC,EAAE,CAAC,SAAS,CAAC,CAAC;IACjD,CAAC;IAED,OAAO,MAAM,CAAC;AAChB,CAAC"}
1
+ {"version":3,"file":"textDeletes.js","sourceRoot":"","sources":["../../../src/client/coalescence/textDeletes.ts"],"names":[],"mappings":"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"}
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@vuer-ai/vuer-rtc",
3
- "version": "0.5.3",
3
+ "version": "0.5.4",
4
4
  "private": false,
5
5
  "type": "module",
6
6
  "description": "CRDT-based real-time collaborative data structures",
@@ -14,12 +14,33 @@
14
14
  import type { TextOperation } from './textTypes.js';
15
15
  import type { InsertOp } from '../crdt/Rope.js';
16
16
  import { parseItemId } from '../crdt/Rope.js';
17
+ import { optimizeDeletions, parseItemId as parseId } from './coalescence/utils.js';
17
18
 
18
19
  export interface CoalesceOptions {
19
20
  /** Time threshold in milliseconds (default: 1000ms = 1 second) */
20
21
  thresholdMs?: number;
21
22
  }
22
23
 
24
+ /**
25
+ * Sort and optimize deletions array.
26
+ * Deletions must be sorted in ascending order for optimizeDeletions to work correctly.
27
+ */
28
+ function sortAndOptimizeDeletions(deletions: Array<{ id: string; length: number }>): Array<{ id: string; length: number }> {
29
+ if (deletions.length === 0) return deletions;
30
+
31
+ // Sort deletions by agent, then by sequence number (ascending)
32
+ const sorted = [...deletions].sort((a, b) => {
33
+ const aId = parseId(a.id);
34
+ const bId = parseId(b.id);
35
+ if (aId.agent !== bId.agent) {
36
+ return aId.agent.localeCompare(bId.agent);
37
+ }
38
+ return aId.seq - bId.seq;
39
+ });
40
+
41
+ return optimizeDeletions(sorted);
42
+ }
43
+
23
44
  /**
24
45
  * Coalesce consecutive text insert operations.
25
46
  *
@@ -47,6 +68,8 @@ export function coalesceTextOperations(
47
68
  if (op.type === 'insert') {
48
69
  // Flush any pending delete before starting new insert
49
70
  if (pendingDelete !== null) {
71
+ // Sort and optimize deletions array before flushing
72
+ pendingDelete.op.deletions = sortAndOptimizeDeletions(pendingDelete.op.deletions);
50
73
  result.push(pendingDelete);
51
74
  pendingDelete = null;
52
75
  }
@@ -137,6 +160,8 @@ export function coalesceTextOperations(
137
160
  pendingInsert = null;
138
161
  }
139
162
  if (pendingDelete !== null) {
163
+ // Sort and optimize deletions array before flushing
164
+ pendingDelete.op.deletions = sortAndOptimizeDeletions(pendingDelete.op.deletions);
140
165
  result.push(pendingDelete);
141
166
  pendingDelete = null;
142
167
  }
@@ -149,6 +174,8 @@ export function coalesceTextOperations(
149
174
  result.push(pendingInsert);
150
175
  }
151
176
  if (pendingDelete !== null) {
177
+ // Sort and optimize deletions array before flushing
178
+ pendingDelete.op.deletions = sortAndOptimizeDeletions(pendingDelete.op.deletions);
152
179
  result.push(pendingDelete);
153
180
  }
154
181
 
@@ -5,7 +5,7 @@
5
5
  */
6
6
 
7
7
  import type { Operation } from '../../operations/OperationTypes.js';
8
- import { optimizeDeletions } from './utils.js';
8
+ import { optimizeDeletions, parseItemId } from './utils.js';
9
9
 
10
10
  export interface TextDeleteOp {
11
11
  otype: 'text.delete';
@@ -21,6 +21,26 @@ export interface CoalesceOptions {
21
21
  thresholdMs?: number;
22
22
  }
23
23
 
24
+ /**
25
+ * Sort and optimize deletions array.
26
+ * Deletions must be sorted in ascending order for optimizeDeletions to work correctly.
27
+ */
28
+ function sortAndOptimizeDeletions(deletions: Array<{ id: string; length: number }>): Array<{ id: string; length: number }> {
29
+ if (deletions.length === 0) return deletions;
30
+
31
+ // Sort deletions by agent, then by sequence number (ascending)
32
+ const sorted = [...deletions].sort((a, b) => {
33
+ const aId = parseItemId(a.id);
34
+ const bId = parseItemId(b.id);
35
+ if (aId.agent !== bId.agent) {
36
+ return aId.agent.localeCompare(bId.agent);
37
+ }
38
+ return aId.seq - bId.seq;
39
+ });
40
+
41
+ return optimizeDeletions(sorted);
42
+ }
43
+
24
44
  /**
25
45
  * Check if an operation is a text delete with CRDT metadata
26
46
  */
@@ -83,14 +103,14 @@ export function coalesceTextDeletes(
83
103
 
84
104
  // Flush any remaining pending delete
85
105
  if (pending !== null) {
86
- // Optimize deletions array before pushing
87
- pending.deletions = optimizeDeletions(pending.deletions);
106
+ // Sort and optimize deletions array before pushing
107
+ pending.deletions = sortAndOptimizeDeletions(pending.deletions);
88
108
  result.push(pending);
89
109
  }
90
110
 
91
- // Also optimize deletions in all previously pushed operations
111
+ // Also sort and optimize deletions in all previously pushed operations
92
112
  for (const op of result) {
93
- op.deletions = optimizeDeletions(op.deletions);
113
+ op.deletions = sortAndOptimizeDeletions(op.deletions);
94
114
  }
95
115
 
96
116
  return result;
@@ -298,6 +298,105 @@ describe('coalesceTextOperations', () => {
298
298
  });
299
299
  });
300
300
 
301
+ describe('delete operations coalescence', () => {
302
+ it('should merge consecutive delete operations and optimize deletions array', () => {
303
+ // Simulate pressing backspace 7 times (7 separate delete operations)
304
+ const ops: TextOperation[] = [
305
+ {
306
+ type: 'delete',
307
+ op: {
308
+ deletions: [{ id: 'alice:7', length: 1 }],
309
+ },
310
+ } as any,
311
+ {
312
+ type: 'delete',
313
+ op: {
314
+ deletions: [{ id: 'alice:6', length: 1 }],
315
+ },
316
+ } as any,
317
+ {
318
+ type: 'delete',
319
+ op: {
320
+ deletions: [{ id: 'alice:5', length: 1 }],
321
+ },
322
+ } as any,
323
+ {
324
+ type: 'delete',
325
+ op: {
326
+ deletions: [{ id: 'alice:4', length: 1 }],
327
+ },
328
+ } as any,
329
+ {
330
+ type: 'delete',
331
+ op: {
332
+ deletions: [{ id: 'alice:3', length: 1 }],
333
+ },
334
+ } as any,
335
+ {
336
+ type: 'delete',
337
+ op: {
338
+ deletions: [{ id: 'alice:2', length: 1 }],
339
+ },
340
+ } as any,
341
+ {
342
+ type: 'delete',
343
+ op: {
344
+ deletions: [{ id: 'alice:1', length: 1 }],
345
+ },
346
+ } as any,
347
+ ];
348
+
349
+ const result = coalesceTextOperations(ops, { thresholdMs: 1000 });
350
+
351
+ // Should merge into ONE delete operation
352
+ expect(result).toHaveLength(1);
353
+ expect(result[0].type).toBe('delete');
354
+
355
+ // CRITICAL: Deletions array should be optimized (7 entries → 1 entry)
356
+ const deleteOp = result[0] as any;
357
+ expect(deleteOp.op.deletions).toBeDefined();
358
+ expect(deleteOp.op.deletions).toHaveLength(1);
359
+ expect(deleteOp.op.deletions[0]).toEqual({ id: 'alice:1', length: 7 });
360
+ });
361
+
362
+ it('should optimize deletions with gaps', () => {
363
+ // Deletions with non-consecutive IDs (can't be fully merged)
364
+ const ops: TextOperation[] = [
365
+ {
366
+ type: 'delete',
367
+ op: {
368
+ deletions: [
369
+ { id: 'alice:5', length: 1 },
370
+ { id: 'alice:4', length: 1 },
371
+ ],
372
+ },
373
+ } as any,
374
+ {
375
+ type: 'delete',
376
+ op: {
377
+ deletions: [
378
+ { id: 'alice:2', length: 1 }, // Gap! alice:3 is missing
379
+ { id: 'alice:1', length: 1 },
380
+ ],
381
+ },
382
+ } as any,
383
+ ];
384
+
385
+ const result = coalesceTextOperations(ops, { thresholdMs: 1000 });
386
+
387
+ // Should merge into ONE delete operation
388
+ expect(result).toHaveLength(1);
389
+ expect(result[0].type).toBe('delete');
390
+
391
+ // Deletions should be optimized: [alice:4-5 (len 2), alice:1-2 (len 2)]
392
+ const deleteOp = result[0] as any;
393
+ expect(deleteOp.op.deletions).toBeDefined();
394
+ expect(deleteOp.op.deletions).toHaveLength(2); // Two ranges due to gap
395
+ expect(deleteOp.op.deletions).toContainEqual({ id: 'alice:4', length: 2 });
396
+ expect(deleteOp.op.deletions).toContainEqual({ id: 'alice:1', length: 2 });
397
+ });
398
+ });
399
+
301
400
  describe('edge cases', () => {
302
401
  it('should handle empty operations array', () => {
303
402
  const result = coalesceTextOperations([]);
@@ -235,15 +235,15 @@ describe('Delete Coalescence Bug', () => {
235
235
  expect(coalesced).toHaveLength(1);
236
236
  expect(coalesced[0].type).toBe('delete');
237
237
 
238
- // The deletions array should already be efficient from remove() optimization
238
+ // FIXED in v0.5.4: coalesceTextOperations now sorts and optimizes deletions!
239
239
  // Each individual remove() creates 1 deletion (only deleting 1 char)
240
- // When coalesceTextOperations merges the 7 ops, it combines the deletions arrays
240
+ // When coalesceTextOperations merges the 7 ops, it:
241
+ // 1. Combines the deletions arrays: [{id:Alice:6,len:1}, ..., {id:Alice:0,len:1}]
242
+ // 2. Sorts them: [{id:Alice:0,len:1}, ..., {id:Alice:6,len:1}]
243
+ // 3. Optimizes them: [{id:Alice:0,len:7}]
241
244
  const coalescedOp = (coalesced[0] as any).op;
242
- expect(coalescedOp.deletions).toHaveLength(7);
243
-
244
- // Note: These 7 deletions are NOT consecutive because we deleted backwards (6→0)
245
- // So they cannot be merged. If we add optimizeDeletions() to coalesceTextOperations,
246
- // it would merge them if they were consecutive.
245
+ expect(coalescedOp.deletions).toHaveLength(1);
246
+ expect(coalescedOp.deletions[0]).toEqual({ id: 'Alice:0', length: 7 });
247
247
  });
248
248
  });
249
249
  });