@vuer-ai/vuer-rtc 0.7.0 → 0.8.2

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 (172) hide show
  1. package/CLAUDE.md +3 -2
  2. package/dist/client/EditBuffer.d.ts +4 -4
  3. package/dist/client/EditBuffer.d.ts.map +1 -1
  4. package/dist/client/EditBuffer.js +26 -25
  5. package/dist/client/EditBuffer.js.map +1 -1
  6. package/dist/client/actions.d.ts +3 -3
  7. package/dist/client/actions.d.ts.map +1 -1
  8. package/dist/client/actions.js +71 -70
  9. package/dist/client/actions.js.map +1 -1
  10. package/dist/client/coalesceGraphOps.d.ts +4 -4
  11. package/dist/client/coalesceGraphOps.js +4 -4
  12. package/dist/client/coalesceTextOperations.d.ts.map +1 -1
  13. package/dist/client/coalesceTextOperations.js +23 -20
  14. package/dist/client/coalesceTextOperations.js.map +1 -1
  15. package/dist/client/coalescence/lwwOperations.js +3 -3
  16. package/dist/client/coalescence/lwwOperations.js.map +1 -1
  17. package/dist/client/coalescence/numberOperations.js +2 -2
  18. package/dist/client/coalescence/numberOperations.js.map +1 -1
  19. package/dist/client/coalescence/registry.d.ts +3 -3
  20. package/dist/client/coalescence/registry.d.ts.map +1 -1
  21. package/dist/client/coalescence/registry.js +11 -11
  22. package/dist/client/coalescence/registry.js.map +1 -1
  23. package/dist/client/coalescence/textDeletes.d.ts +8 -7
  24. package/dist/client/coalescence/textDeletes.d.ts.map +1 -1
  25. package/dist/client/coalescence/textDeletes.js +11 -11
  26. package/dist/client/coalescence/textDeletes.js.map +1 -1
  27. package/dist/client/coalescence/textInserts.d.ts +8 -5
  28. package/dist/client/coalescence/textInserts.d.ts.map +1 -1
  29. package/dist/client/coalescence/textInserts.js +32 -12
  30. package/dist/client/coalescence/textInserts.js.map +1 -1
  31. package/dist/client/coalescence/utils.d.ts +3 -9
  32. package/dist/client/coalescence/utils.d.ts.map +1 -1
  33. package/dist/client/coalescence/utils.js +10 -8
  34. package/dist/client/coalescence/utils.js.map +1 -1
  35. package/dist/client/coalescence/vector3Operations.js +2 -2
  36. package/dist/client/coalescence/vector3Operations.js.map +1 -1
  37. package/dist/client/createGraph.d.ts +2 -2
  38. package/dist/client/createGraph.js +4 -4
  39. package/dist/client/createGraph.js.map +1 -1
  40. package/dist/client/createTextDocument.d.ts +1 -1
  41. package/dist/client/createTextDocument.js +3 -3
  42. package/dist/client/createTextDocument.js.map +1 -1
  43. package/dist/client/hooks.d.ts +3 -3
  44. package/dist/client/hooks.d.ts.map +1 -1
  45. package/dist/client/hooks.js +4 -4
  46. package/dist/client/hooks.js.map +1 -1
  47. package/dist/client/textActions.d.ts +2 -2
  48. package/dist/client/textActions.d.ts.map +1 -1
  49. package/dist/client/textActions.js +47 -47
  50. package/dist/client/textActions.js.map +1 -1
  51. package/dist/client/textTypes.d.ts +8 -8
  52. package/dist/client/textTypes.d.ts.map +1 -1
  53. package/dist/client/types.d.ts +4 -4
  54. package/dist/client/types.d.ts.map +1 -1
  55. package/dist/crdt/GraphTextCRDT.d.ts +2 -2
  56. package/dist/crdt/GraphTextCRDT.d.ts.map +1 -1
  57. package/dist/crdt/GraphTextCRDT.js +6 -6
  58. package/dist/crdt/GraphTextCRDT.js.map +1 -1
  59. package/dist/crdt/Rope.d.ts +13 -14
  60. package/dist/crdt/Rope.d.ts.map +1 -1
  61. package/dist/crdt/Rope.js +130 -59
  62. package/dist/crdt/Rope.js.map +1 -1
  63. package/dist/crdt/index.d.ts +1 -1
  64. package/dist/crdt/index.d.ts.map +1 -1
  65. package/dist/crdt/index.js +1 -1
  66. package/dist/crdt/index.js.map +1 -1
  67. package/dist/index.d.ts +1 -1
  68. package/dist/index.d.ts.map +1 -1
  69. package/dist/index.js +1 -1
  70. package/dist/index.js.map +1 -1
  71. package/dist/operations/OperationTypes.d.ts +45 -48
  72. package/dist/operations/OperationTypes.d.ts.map +1 -1
  73. package/dist/operations/OperationValidator.js +11 -11
  74. package/dist/operations/OperationValidator.js.map +1 -1
  75. package/dist/operations/apply/node.js +3 -3
  76. package/dist/operations/apply/node.js.map +1 -1
  77. package/dist/operations/apply/text.d.ts.map +1 -1
  78. package/dist/operations/apply/text.js +35 -32
  79. package/dist/operations/apply/text.js.map +1 -1
  80. package/dist/operations/apply/types.d.ts +4 -4
  81. package/dist/operations/apply/types.d.ts.map +1 -1
  82. package/dist/operations/apply/types.js +8 -8
  83. package/dist/operations/apply/types.js.map +1 -1
  84. package/dist/operations/dispatcher.d.ts.map +1 -1
  85. package/dist/operations/dispatcher.js +52 -13
  86. package/dist/operations/dispatcher.js.map +1 -1
  87. package/dist/serdes.d.ts +1 -1
  88. package/dist/serdes.d.ts.map +1 -1
  89. package/dist/state/ConflictResolver.d.ts +9 -9
  90. package/dist/state/ConflictResolver.d.ts.map +1 -1
  91. package/dist/state/ConflictResolver.js +20 -20
  92. package/dist/state/ConflictResolver.js.map +1 -1
  93. package/dist/state/DType.d.ts +2 -2
  94. package/dist/state/DType.d.ts.map +1 -1
  95. package/dist/state/DType.js +14 -14
  96. package/dist/state/DType.js.map +1 -1
  97. package/dist/state/VectorClock.d.ts +6 -6
  98. package/dist/state/VectorClock.d.ts.map +1 -1
  99. package/dist/state/VectorClock.js +14 -14
  100. package/dist/state/VectorClock.js.map +1 -1
  101. package/dist/state/index.d.ts +1 -1
  102. package/dist/state/index.js +1 -1
  103. package/examples/01-basic-usage.ts +16 -16
  104. package/examples/02-concurrent-edits.ts +29 -29
  105. package/examples/03-scene-building.ts +28 -28
  106. package/examples/04-conflict-resolution.ts +56 -56
  107. package/examples/05-coalescence-usage.ts +23 -23
  108. package/examples/README.md +12 -12
  109. package/package.json +1 -1
  110. package/src/client/EditBuffer.ts +28 -27
  111. package/src/client/TEXT_DOCUMENT_API.md +9 -9
  112. package/src/client/actions.ts +74 -70
  113. package/src/client/coalesceGraphOps.ts +4 -4
  114. package/src/client/coalesceTextOperations.ts +26 -22
  115. package/src/client/coalescence/lwwOperations.ts +3 -3
  116. package/src/client/coalescence/numberOperations.ts +2 -2
  117. package/src/client/coalescence/registry.ts +13 -12
  118. package/src/client/coalescence/textDeletes.ts +22 -18
  119. package/src/client/coalescence/textInserts.ts +49 -25
  120. package/src/client/coalescence/utils.ts +14 -11
  121. package/src/client/coalescence/vector3Operations.ts +2 -2
  122. package/src/client/createGraph.ts +4 -4
  123. package/src/client/createTextDocument.ts +3 -3
  124. package/src/client/hooks.tsx +5 -5
  125. package/src/client/textActions.ts +47 -47
  126. package/src/client/textTypes.ts +8 -8
  127. package/src/client/types.ts +4 -4
  128. package/src/crdt/GraphTextCRDT.ts +6 -6
  129. package/src/crdt/Rope.ts +156 -71
  130. package/src/crdt/index.ts +2 -0
  131. package/src/index.ts +2 -0
  132. package/src/operations/OperationTypes.ts +47 -47
  133. package/src/operations/OperationValidator.ts +11 -11
  134. package/src/operations/apply/node.ts +3 -3
  135. package/src/operations/apply/text.ts +38 -32
  136. package/src/operations/apply/types.ts +11 -11
  137. package/src/operations/dispatcher.ts +57 -13
  138. package/src/serdes.ts +1 -1
  139. package/src/state/ConflictResolver.ts +23 -23
  140. package/src/state/DType.ts +16 -16
  141. package/src/state/VectorClock.ts +14 -14
  142. package/src/state/index.ts +1 -1
  143. package/tests/client/actions.test.ts +76 -76
  144. package/tests/client/coalesce-graph-operations.test.ts +84 -84
  145. package/tests/client/coalesce-text-operations.test.ts +91 -114
  146. package/tests/client/compaction.test.ts +18 -18
  147. package/tests/client/delete-coalescence-bug.test.ts +34 -34
  148. package/tests/client/edit-buffer.test.ts +27 -30
  149. package/tests/client/graph-coalescence-phase1.test.ts +66 -66
  150. package/tests/client/graph-coalescence.test.ts +50 -50
  151. package/tests/client/journal-benchmark.test.ts +5 -5
  152. package/tests/crdt/graph-text-crdt.test.ts +60 -64
  153. package/tests/crdt/rope.test.ts +9 -8
  154. package/tests/crdt/text-operations.test.ts +28 -28
  155. package/tests/fixtures/array-ops.jsonl +6 -6
  156. package/tests/fixtures/boolean-ops.jsonl +6 -6
  157. package/tests/fixtures/color-ops.jsonl +4 -4
  158. package/tests/fixtures/edit-buffer.jsonl +3 -3
  159. package/tests/fixtures/node-ops.jsonl +6 -6
  160. package/tests/fixtures/number-ops.jsonl +7 -7
  161. package/tests/fixtures/object-ops.jsonl +4 -4
  162. package/tests/fixtures/operations.jsonl +7 -7
  163. package/tests/fixtures/string-ops.jsonl +4 -4
  164. package/tests/fixtures/undo-redo.jsonl +3 -3
  165. package/tests/fixtures/vector-ops.jsonl +17 -17
  166. package/tests/operations/collections.test.ts +4 -4
  167. package/tests/operations/nodes.test.ts +5 -5
  168. package/tests/operations/operation-ordering.test.ts +406 -0
  169. package/tests/operations/primitives.test.ts +4 -4
  170. package/tests/operations/unified-schema.test.ts +27 -27
  171. package/tests/operations/vectors.test.ts +4 -4
  172. package/tests/sync/digest.test.ts +5 -5
@@ -98,7 +98,7 @@ describe('GraphTextCRDT', () => {
98
98
  expect(op.id).toBeDefined();
99
99
  expect(typeof op.id).toBe('string');
100
100
  expect(op.id).toMatch(/^session-1:/); // String format: "agent:seq"
101
- expect(op.content).toBe('test');
101
+ expect(op.value[1]).toBe('test');
102
102
  expect(op.seq).toBeDefined();
103
103
  expect(op.ts).toBeDefined();
104
104
  });
@@ -142,15 +142,15 @@ describe('GraphTextCRDT', () => {
142
142
 
143
143
  it('should return DeleteOp with deletion metadata', () => {
144
144
  const op = crdt.deleteLocal('node-1', 'content', 0, 5);
145
- expect(op.deletions).toBeDefined();
146
- expect(op.deletions.length).toBeGreaterThan(0);
147
- expect(op.deletions[0].id).toBeDefined();
148
- expect(op.deletions[0].length).toBeDefined();
145
+ expect(op.rm).toBeDefined();
146
+ expect(op.rm.length).toBeGreaterThan(0);
147
+ expect(op.rm[0][0]).toBeDefined();
148
+ expect(op.rm[0][1]).toBeDefined();
149
149
  });
150
150
 
151
151
  it('should handle delete of zero length', () => {
152
152
  const op = crdt.deleteLocal('node-1', 'content', 0, 0);
153
- expect(op.deletions).toHaveLength(0);
153
+ expect(op.rm).toHaveLength(0);
154
154
  expect(crdt.getText('node-1', 'content')).toBe('Hello World');
155
155
  });
156
156
 
@@ -161,7 +161,7 @@ describe('GraphTextCRDT', () => {
161
161
 
162
162
  it('should auto-create rope if not initialized', () => {
163
163
  const op = crdt.deleteLocal('node-2', 'title', 0, 5);
164
- expect(op.deletions).toHaveLength(0);
164
+ expect(op.rm).toHaveLength(0);
165
165
  });
166
166
  });
167
167
 
@@ -180,9 +180,9 @@ describe('GraphTextCRDT', () => {
180
180
 
181
181
  it('should return ReplaceOp with delete and insert metadata', () => {
182
182
  const op = crdt.replaceLocal('node-1', 'content', 0, 5, 'Hi');
183
- expect(op.delete).toBeDefined();
184
- expect(op.insert).toBeDefined();
185
- expect(op.insert.content).toBe('Hi');
183
+ expect(op.rm).toBeDefined();
184
+ expect(op.id).toBeDefined();
185
+ expect(op.value[1]).toBe('Hi');
186
186
  });
187
187
 
188
188
  it('should handle delete-only replacement (empty insert)', () => {
@@ -203,7 +203,7 @@ describe('GraphTextCRDT', () => {
203
203
  it('should auto-create rope if not initialized', () => {
204
204
  const op = crdt.replaceLocal('node-2', 'title', 0, 0, 'Title');
205
205
  expect(crdt.getText('node-2', 'title')).toBe('Title');
206
- expect(op.insert.content).toBe('Title');
206
+ expect(op.value[1]).toBe('Title');
207
207
  });
208
208
  });
209
209
 
@@ -217,9 +217,9 @@ describe('GraphTextCRDT', () => {
217
217
 
218
218
  it('should apply remote insert operation', () => {
219
219
  const op: InsertOp = {
220
+ ot: 'insert',
220
221
  id: 'session-2:0',
221
- content: 'Hello',
222
- parentId: null,
222
+ value: [null, 'Hello'],
223
223
  seq: 1,
224
224
  ts: Date.now() / 1000,
225
225
  };
@@ -229,18 +229,18 @@ describe('GraphTextCRDT', () => {
229
229
 
230
230
  it('should apply remote insert with parent', () => {
231
231
  const op1: InsertOp = {
232
+ ot: 'insert',
232
233
  id: 'session-1:0',
233
- content: 'Hello',
234
- parentId: null,
234
+ value: [null, 'Hello'],
235
235
  seq: 1,
236
236
  ts: Date.now() / 1000,
237
237
  };
238
238
  crdt.applyInsert('node-1', 'content', op1);
239
239
 
240
240
  const op2: InsertOp = {
241
+ ot: 'insert',
241
242
  id: 'session-2:0',
242
- content: ' World',
243
- parentId: 'session-1:4',
243
+ value: ['session-1:4', ' World'],
244
244
  seq: 2,
245
245
  ts: Date.now() / 1000,
246
246
  };
@@ -250,9 +250,9 @@ describe('GraphTextCRDT', () => {
250
250
 
251
251
  it('should auto-create rope if not initialized', () => {
252
252
  const op: InsertOp = {
253
+ ot: 'insert',
253
254
  id: 'session-2:0',
254
- content: 'test',
255
- parentId: null,
255
+ value: [null, 'test'],
256
256
  seq: 1,
257
257
  ts: Date.now() / 1000,
258
258
  };
@@ -264,9 +264,9 @@ describe('GraphTextCRDT', () => {
264
264
  const op1 = crdt.insertLocal('node-1', 'content', 0, 'A');
265
265
 
266
266
  const op2: InsertOp = {
267
+ ot: 'insert',
267
268
  id: 'session-2:0',
268
- content: 'B',
269
- parentId: null,
269
+ value: [null, 'B'],
270
270
  seq: 1,
271
271
  ts: Date.now() / 1000,
272
272
  };
@@ -293,21 +293,26 @@ describe('GraphTextCRDT', () => {
293
293
  expect(items.length).toBeGreaterThan(0);
294
294
 
295
295
  const op: DeleteOp = {
296
- deletions: [{ id: items[0].id, length: 5 }],
296
+ ot: 'delete',
297
+ rm: [[items[0].id, 5]],
297
298
  };
298
299
  crdt.applyDelete('node-1', 'content', op);
299
300
  expect(crdt.getText('node-1', 'content')).toBe(' World');
300
301
  });
301
302
 
302
303
  it('should handle empty deletion list', () => {
303
- const op: DeleteOp = { deletions: [] };
304
+ const op: DeleteOp = {
305
+ ot: 'delete',
306
+ rm: []
307
+ };
304
308
  crdt.applyDelete('node-1', 'content', op);
305
309
  expect(crdt.getText('node-1', 'content')).toBe('Hello World');
306
310
  });
307
311
 
308
312
  it('should auto-create rope if not initialized', () => {
309
313
  const op: DeleteOp = {
310
- deletions: [{ id: 'session-1:0', length: 5 }],
314
+ ot: 'delete',
315
+ rm: [['session-1:0', 5]],
311
316
  };
312
317
  crdt.applyDelete('node-2', 'title', op);
313
318
  expect(crdt.getText('node-2', 'title')).toBe('');
@@ -317,7 +322,8 @@ describe('GraphTextCRDT', () => {
317
322
  const delOp = crdt.deleteLocal('node-1', 'content', 0, 5);
318
323
 
319
324
  const remoteOp: DeleteOp = {
320
- deletions: [{ id: 'session-1:5', length: 6 }],
325
+ ot: 'delete',
326
+ rm: [['session-1:5', 6]],
321
327
  };
322
328
  crdt.applyDelete('node-1', 'content', remoteOp);
323
329
 
@@ -338,16 +344,12 @@ describe('GraphTextCRDT', () => {
338
344
  const items = getItems(rope);
339
345
 
340
346
  const op: ReplaceOp = {
341
- delete: {
342
- deletions: [{ id: items[0].id, length: 5 }],
343
- },
344
- insert: {
345
- id: 'session-2:0',
346
- content: 'Hi',
347
- parentId: null,
348
- seq: 2,
349
- ts: Date.now() / 1000,
350
- },
347
+ ot: 'replace',
348
+ rm: [[items[0].id, 5]],
349
+ id: 'session-2:0',
350
+ value: [null, 'Hi'],
351
+ seq: 2,
352
+ ts: Date.now() / 1000,
351
353
  };
352
354
  crdt.applyReplace('node-1', 'content', op);
353
355
  expect(crdt.getText('node-1', 'content')).toBe('Hi World');
@@ -358,16 +360,12 @@ describe('GraphTextCRDT', () => {
358
360
  const items = getItems(rope);
359
361
 
360
362
  const op: ReplaceOp = {
361
- delete: {
362
- deletions: [{ id: items[0].id, length: 6 }],
363
- },
364
- insert: {
365
- id: 'session-2:0',
366
- content: '',
367
- parentId: null,
368
- seq: 2,
369
- ts: Date.now() / 1000,
370
- },
363
+ ot: 'replace',
364
+ rm: [[items[0].id, 6]],
365
+ id: 'session-2:0',
366
+ value: [null, ''],
367
+ seq: 2,
368
+ ts: Date.now() / 1000,
371
369
  };
372
370
  crdt.applyReplace('node-1', 'content', op);
373
371
  expect(crdt.getText('node-1', 'content')).toBe('World');
@@ -375,14 +373,12 @@ describe('GraphTextCRDT', () => {
375
373
 
376
374
  it('should auto-create rope if not initialized', () => {
377
375
  const op: ReplaceOp = {
378
- delete: { deletions: [] },
379
- insert: {
380
- id: 'session-2:0',
381
- content: 'new',
382
- parentId: null,
383
- seq: 1,
384
- ts: Date.now() / 1000,
385
- },
376
+ ot: 'replace',
377
+ rm: [],
378
+ id: 'session-2:0',
379
+ value: [null, 'new'],
380
+ seq: 1,
381
+ ts: Date.now() / 1000,
386
382
  };
387
383
  crdt.applyReplace('node-2', 'title', op);
388
384
  expect(crdt.getText('node-2', 'title')).toBe('new');
@@ -571,9 +567,9 @@ describe('GraphTextCRDT', () => {
571
567
 
572
568
  // Initialize both from the same initial state
573
569
  const initOp: InsertOp = {
570
+ ot: 'insert',
574
571
  id: 'init:0',
575
- content: 'Hello',
576
- parentId: null,
572
+ value: [null, 'Hello'],
577
573
  seq: 1,
578
574
  ts: Date.now() / 1000,
579
575
  };
@@ -600,9 +596,9 @@ describe('GraphTextCRDT', () => {
600
596
 
601
597
  // Initialize from same initial state
602
598
  const initOp: InsertOp = {
599
+ ot: 'insert',
603
600
  id: 'init:0',
604
- content: 'ABCDEF',
605
- parentId: null,
601
+ value: [null, 'ABCDEF'],
606
602
  seq: 1,
607
603
  ts: Date.now() / 1000,
608
604
  };
@@ -626,9 +622,9 @@ describe('GraphTextCRDT', () => {
626
622
 
627
623
  // Initialize from same initial state
628
624
  const initOp: InsertOp = {
625
+ ot: 'insert',
629
626
  id: 'init:0',
630
- content: 'test',
631
- parentId: null,
627
+ value: [null, 'test'],
632
628
  seq: 1,
633
629
  ts: Date.now() / 1000,
634
630
  };
@@ -645,9 +641,9 @@ describe('GraphTextCRDT', () => {
645
641
 
646
642
  // Apply ops from crdt2 to crdt1
647
643
  for (const op of ops2) {
648
- if ('content' in op) {
644
+ if (op.ot === 'insert') {
649
645
  crdt1.applyInsert('doc-1', 'content', op as InsertOp);
650
- } else if ('deletions' in op) {
646
+ } else if (op.ot === 'delete') {
651
647
  crdt1.applyDelete('doc-1', 'content', op as DeleteOp);
652
648
  } else {
653
649
  crdt1.applyReplace('doc-1', 'content', op as ReplaceOp);
@@ -656,9 +652,9 @@ describe('GraphTextCRDT', () => {
656
652
 
657
653
  // Apply ops from crdt1 to crdt2
658
654
  for (const op of ops1) {
659
- if ('content' in op) {
655
+ if (op.ot === 'insert') {
660
656
  crdt2.applyInsert('doc-1', 'content', op as InsertOp);
661
- } else if ('deletions' in op) {
657
+ } else if (op.ot === 'delete') {
662
658
  crdt2.applyDelete('doc-1', 'content', op as DeleteOp);
663
659
  } else {
664
660
  crdt2.applyReplace('doc-1', 'content', op as ReplaceOp);
@@ -682,7 +678,7 @@ describe('GraphTextCRDT', () => {
682
678
  crdt.init('node-1', 'content', 'test');
683
679
  const op = crdt.insertLocal('node-1', 'content', 0, '');
684
680
  expect(crdt.getText('node-1', 'content')).toBe('test');
685
- expect(op.content).toBe('');
681
+ expect(op.value[1]).toBe('');
686
682
  });
687
683
 
688
684
  it('should handle very long text', () => {
@@ -170,8 +170,8 @@ describe('TextRope', () => {
170
170
  remove(rope1, 0, 1); // delete 'a'
171
171
  remove(rope2, 2, 1); // delete 'c'
172
172
 
173
- applyDelete(rope1, { deletions: [{ id: opC.id, length: 1 }] });
174
- applyDelete(rope2, { deletions: [{ id: opA.id, length: 1 }] });
173
+ applyDelete(rope1, { ot: 'delete', rm: [[opC.id, 1]] });
174
+ applyDelete(rope2, { ot: 'delete', rm: [[opA.id, 1]] });
175
175
 
176
176
  expect(getText(rope1)).toBe('b');
177
177
  expect(getText(rope2)).toBe('b');
@@ -224,8 +224,9 @@ describe('TextRope', () => {
224
224
 
225
225
  // Critical assertion: Alice's "range" should have Alice's last character as parent
226
226
  // NOT Bob's character, even though Bob's content is in the merged rope
227
- const aliceLastCharSeq = parseItemId(aliceOp1.id).seq + aliceOp1.content.length - 1;
228
- const aliceOp2Parent = aliceOp2.parentId ? parseItemId(aliceOp2.parentId) : null;
227
+ const aliceLastCharSeq = parseItemId(aliceOp1.id).seq + aliceOp1.value[1].length - 1;
228
+ const anchor = aliceOp2.value[0]; // anchor is at [0]
229
+ const aliceOp2Parent = anchor ? parseItemId(anchor) : null;
229
230
 
230
231
  expect(aliceOp2Parent).not.toBeNull();
231
232
  expect(aliceOp2Parent!.agent).toBe('Alice'); // Parent should be Alice's agent
@@ -407,7 +408,7 @@ describe('TextRope', () => {
407
408
  insert(rope, 0, 'hello');
408
409
  const deleted = remove(rope, 2, 0);
409
410
  expect(getText(rope)).toBe('hello');
410
- expect(deleted.deletions).toHaveLength(0);
411
+ expect(deleted.rm).toHaveLength(0);
411
412
  });
412
413
 
413
414
  it('should handle delete past end', () => {
@@ -562,8 +563,8 @@ describe('TextRope', () => {
562
563
 
563
564
  const op = replace(rope, 0, 5, 'world');
564
565
 
565
- expect(op.delete.deletions.length).toBeGreaterThan(0);
566
- expect(op.insert.content).toBe('world');
566
+ expect(op.rm.length).toBeGreaterThan(0);
567
+ expect(op.value[1]).toBe('world'); // content is at [1]
567
568
  });
568
569
 
569
570
  it('should work with applyReplace on remote rope', () => {
@@ -1132,7 +1133,7 @@ describe('TextRope', () => {
1132
1133
  apply(rope1, ins2);
1133
1134
  apply(rope2, ins1);
1134
1135
  // Also need to sync the delete
1135
- const delOp = { deletions: [{ id: 'agent-1:2', length: 2 }] };
1136
+ const delOp: DeleteOp = { ot: 'delete', rm: [['agent-1:2', 2]] };
1136
1137
  applyDelete(rope2, delOp);
1137
1138
 
1138
1139
  expect(getText(rope1)).toBe(getText(rope2));
@@ -18,13 +18,13 @@ import type {
18
18
  TextDeleteOp,
19
19
  } from '../../src/operations/OperationTypes.js';
20
20
 
21
- function createMsg(op: Operation, sessionId = 'test-session', lamport = 1): CRDTMessage {
21
+ function createMsg(op: Operation, client = 'test-session', lamport = 1): CRDTMessage {
22
22
  return {
23
23
  id: `msg-${Math.random().toString(36).slice(2)}`,
24
- sessionId,
25
- clock: { [sessionId]: lamport },
26
- lamportTime: lamport,
27
- timestamp: Date.now() / 1000,
24
+ client,
25
+ clock: { [client]: lamport },
26
+ lt: lamport,
27
+ ts: Date.now() / 1000,
28
28
  ops: [op],
29
29
  };
30
30
  }
@@ -39,7 +39,7 @@ function getText(value: any): string {
39
39
  function createNode(graph: SceneGraph, key: string): SceneGraph {
40
40
  const nodeOp: Operation = {
41
41
  key: '',
42
- otype: 'node.insert',
42
+ ot: 'node.insert',
43
43
  path: 'children',
44
44
  value: {
45
45
  key,
@@ -62,7 +62,7 @@ describe('Text CRDT Operations', () => {
62
62
  it('should initialize an empty text property', () => {
63
63
  const op: TextInitOp = {
64
64
  key: 'doc-1',
65
- otype: 'text.init',
65
+ ot: 'text.init',
66
66
  path: 'content',
67
67
  };
68
68
  const result = applyMessage(graph, createMsg(op));
@@ -73,7 +73,7 @@ describe('Text CRDT Operations', () => {
73
73
  it('should initialize text with initial content', () => {
74
74
  const op: TextInitOp = {
75
75
  key: 'doc-1',
76
- otype: 'text.init',
76
+ ot: 'text.init',
77
77
  path: 'content',
78
78
  value: 'Hello, World!',
79
79
  };
@@ -88,14 +88,14 @@ describe('Text CRDT Operations', () => {
88
88
  // Initialize
89
89
  let result = applyMessage(graph, createMsg({
90
90
  key: 'doc-1',
91
- otype: 'text.init',
91
+ ot: 'text.init',
92
92
  path: 'content',
93
93
  } as TextInitOp));
94
94
 
95
95
  // Insert
96
96
  result = applyMessage(result, createMsg({
97
97
  key: 'doc-1',
98
- otype: 'text.insert',
98
+ ot: 'text.insert',
99
99
  path: 'content',
100
100
  position: 0,
101
101
  value: 'Hello',
@@ -107,14 +107,14 @@ describe('Text CRDT Operations', () => {
107
107
  it('should insert text at the end', () => {
108
108
  let result = applyMessage(graph, createMsg({
109
109
  key: 'doc-1',
110
- otype: 'text.init',
110
+ ot: 'text.init',
111
111
  path: 'content',
112
112
  value: 'Hello',
113
113
  } as TextInitOp));
114
114
 
115
115
  result = applyMessage(result, createMsg({
116
116
  key: 'doc-1',
117
- otype: 'text.insert',
117
+ ot: 'text.insert',
118
118
  path: 'content',
119
119
  position: 5,
120
120
  value: ' World',
@@ -126,14 +126,14 @@ describe('Text CRDT Operations', () => {
126
126
  it('should insert text in the middle', () => {
127
127
  let result = applyMessage(graph, createMsg({
128
128
  key: 'doc-1',
129
- otype: 'text.init',
129
+ ot: 'text.init',
130
130
  path: 'content',
131
131
  value: 'Hllo',
132
132
  } as TextInitOp));
133
133
 
134
134
  result = applyMessage(result, createMsg({
135
135
  key: 'doc-1',
136
- otype: 'text.insert',
136
+ ot: 'text.insert',
137
137
  path: 'content',
138
138
  position: 1,
139
139
  value: 'e',
@@ -147,14 +147,14 @@ describe('Text CRDT Operations', () => {
147
147
  it('should delete text from the beginning', () => {
148
148
  let result = applyMessage(graph, createMsg({
149
149
  key: 'doc-1',
150
- otype: 'text.init',
150
+ ot: 'text.init',
151
151
  path: 'content',
152
152
  value: 'Hello World',
153
153
  } as TextInitOp));
154
154
 
155
155
  result = applyMessage(result, createMsg({
156
156
  key: 'doc-1',
157
- otype: 'text.delete',
157
+ ot: 'text.delete',
158
158
  path: 'content',
159
159
  position: 0,
160
160
  length: 6,
@@ -166,14 +166,14 @@ describe('Text CRDT Operations', () => {
166
166
  it('should delete text from the end', () => {
167
167
  let result = applyMessage(graph, createMsg({
168
168
  key: 'doc-1',
169
- otype: 'text.init',
169
+ ot: 'text.init',
170
170
  path: 'content',
171
171
  value: 'Hello World',
172
172
  } as TextInitOp));
173
173
 
174
174
  result = applyMessage(result, createMsg({
175
175
  key: 'doc-1',
176
- otype: 'text.delete',
176
+ ot: 'text.delete',
177
177
  path: 'content',
178
178
  position: 5,
179
179
  length: 6,
@@ -185,14 +185,14 @@ describe('Text CRDT Operations', () => {
185
185
  it('should delete text from the middle', () => {
186
186
  let result = applyMessage(graph, createMsg({
187
187
  key: 'doc-1',
188
- otype: 'text.init',
188
+ ot: 'text.init',
189
189
  path: 'content',
190
190
  value: 'Hello World',
191
191
  } as TextInitOp));
192
192
 
193
193
  result = applyMessage(result, createMsg({
194
194
  key: 'doc-1',
195
- otype: 'text.delete',
195
+ ot: 'text.delete',
196
196
  path: 'content',
197
197
  position: 2,
198
198
  length: 6,
@@ -207,14 +207,14 @@ describe('Text CRDT Operations', () => {
207
207
  // User A and User B both start with same content
208
208
  let graphA = applyMessage(graph, createMsg({
209
209
  key: 'doc-1',
210
- otype: 'text.init',
210
+ ot: 'text.init',
211
211
  path: 'content',
212
212
  value: 'Hello',
213
213
  } as TextInitOp, 'user-a', 1));
214
214
 
215
215
  let graphB = applyMessage(graph, createMsg({
216
216
  key: 'doc-1',
217
- otype: 'text.init',
217
+ ot: 'text.init',
218
218
  path: 'content',
219
219
  value: 'Hello',
220
220
  } as TextInitOp, 'user-b', 1));
@@ -222,7 +222,7 @@ describe('Text CRDT Operations', () => {
222
222
  // User A inserts at position 0
223
223
  graphA = applyMessage(graphA, createMsg({
224
224
  key: 'doc-1',
225
- otype: 'text.insert',
225
+ ot: 'text.insert',
226
226
  path: 'content',
227
227
  position: 0,
228
228
  value: 'A',
@@ -231,7 +231,7 @@ describe('Text CRDT Operations', () => {
231
231
  // User B inserts at position 5
232
232
  graphB = applyMessage(graphB, createMsg({
233
233
  key: 'doc-1',
234
- otype: 'text.insert',
234
+ ot: 'text.insert',
235
235
  path: 'content',
236
236
  position: 5,
237
237
  value: 'B',
@@ -250,7 +250,7 @@ describe('Text CRDT Operations', () => {
250
250
  it('should return the underlying TextRope', () => {
251
251
  const result = applyMessage(graph, createMsg({
252
252
  key: 'doc-1',
253
- otype: 'text.init',
253
+ ot: 'text.init',
254
254
  path: 'content',
255
255
  value: 'Test',
256
256
  } as TextInitOp));
@@ -278,7 +278,7 @@ describe('Text CRDT Operations', () => {
278
278
  // Initialize empty document
279
279
  let result = applyMessage(graph, createMsg({
280
280
  key: 'doc-1',
281
- otype: 'text.init',
281
+ ot: 'text.init',
282
282
  path: 'content',
283
283
  } as TextInitOp));
284
284
 
@@ -297,7 +297,7 @@ describe('Text CRDT Operations', () => {
297
297
  if (del > 0) {
298
298
  result = applyMessage(result, createMsg({
299
299
  key: 'doc-1',
300
- otype: 'text.delete',
300
+ ot: 'text.delete',
301
301
  path: 'content',
302
302
  position: pos,
303
303
  length: del,
@@ -308,7 +308,7 @@ describe('Text CRDT Operations', () => {
308
308
  if (ins.length > 0) {
309
309
  result = applyMessage(result, createMsg({
310
310
  key: 'doc-1',
311
- otype: 'text.insert',
311
+ ot: 'text.insert',
312
312
  path: 'content',
313
313
  position: pos,
314
314
  value: ins,
@@ -1,6 +1,6 @@
1
- {"name": "array_set", "op": {"key": "node-1", "otype": "array.set", "path": "tags", "value": ["enemy", "active"]}}
2
- {"name": "array_set_empty", "op": {"key": "node-1", "otype": "array.set", "path": "tags", "value": []}}
3
- {"name": "array_push", "op": {"key": "node-1", "otype": "array.push", "path": "tags", "value": "new-tag"}}
4
- {"name": "array_push_number", "op": {"key": "node-1", "otype": "array.push", "path": "scores", "value": 100}}
5
- {"name": "array_union", "op": {"key": "node-1", "otype": "array.union", "path": "tags", "value": ["tag-a", "tag-b"]}}
6
- {"name": "array_remove", "op": {"key": "node-1", "otype": "array.remove", "path": "tags", "value": "enemy"}}
1
+ {"name": "array_set", "op": {"key": "node-1", "ot": "array.set", "path": "tags", "value": ["enemy", "active"]}}
2
+ {"name": "array_set_empty", "op": {"key": "node-1", "ot": "array.set", "path": "tags", "value": []}}
3
+ {"name": "array_push", "op": {"key": "node-1", "ot": "array.push", "path": "tags", "value": "new-tag"}}
4
+ {"name": "array_push_number", "op": {"key": "node-1", "ot": "array.push", "path": "scores", "value": 100}}
5
+ {"name": "array_union", "op": {"key": "node-1", "ot": "array.union", "path": "tags", "value": ["tag-a", "tag-b"]}}
6
+ {"name": "array_remove", "op": {"key": "node-1", "ot": "array.remove", "path": "tags", "value": "enemy"}}
@@ -1,6 +1,6 @@
1
- {"name": "boolean_set_true", "op": {"key": "node-1", "otype": "boolean.set", "path": "visible", "value": true}}
2
- {"name": "boolean_set_false", "op": {"key": "node-1", "otype": "boolean.set", "path": "visible", "value": false}}
3
- {"name": "boolean_or_true", "op": {"key": "node-1", "otype": "boolean.or", "path": "enabled", "value": true}}
4
- {"name": "boolean_or_false", "op": {"key": "node-1", "otype": "boolean.or", "path": "enabled", "value": false}}
5
- {"name": "boolean_and_true", "op": {"key": "node-1", "otype": "boolean.and", "path": "active", "value": true}}
6
- {"name": "boolean_and_false", "op": {"key": "node-1", "otype": "boolean.and", "path": "active", "value": false}}
1
+ {"name": "boolean_set_true", "op": {"key": "node-1", "ot": "boolean.set", "path": "visible", "value": true}}
2
+ {"name": "boolean_set_false", "op": {"key": "node-1", "ot": "boolean.set", "path": "visible", "value": false}}
3
+ {"name": "boolean_or_true", "op": {"key": "node-1", "ot": "boolean.or", "path": "enabled", "value": true}}
4
+ {"name": "boolean_or_false", "op": {"key": "node-1", "ot": "boolean.or", "path": "enabled", "value": false}}
5
+ {"name": "boolean_and_true", "op": {"key": "node-1", "ot": "boolean.and", "path": "active", "value": true}}
6
+ {"name": "boolean_and_false", "op": {"key": "node-1", "ot": "boolean.and", "path": "active", "value": false}}
@@ -1,4 +1,4 @@
1
- {"name": "color_set_red", "op": {"key": "node-1", "otype": "color.set", "path": "color", "value": "#ff0000"}}
2
- {"name": "color_set_green", "op": {"key": "node-1", "otype": "color.set", "path": "color", "value": "#00ff00"}}
3
- {"name": "color_set_rgb", "op": {"key": "node-1", "otype": "color.set", "path": "color", "value": "#336699"}}
4
- {"name": "color_blend", "op": {"key": "node-1", "otype": "color.blend", "path": "color", "value": "#ffffff"}}
1
+ {"name": "color_set_red", "op": {"key": "node-1", "ot": "color.set", "path": "color", "value": "#ff0000"}}
2
+ {"name": "color_set_green", "op": {"key": "node-1", "ot": "color.set", "path": "color", "value": "#00ff00"}}
3
+ {"name": "color_set_rgb", "op": {"key": "node-1", "ot": "color.set", "path": "color", "value": "#336699"}}
4
+ {"name": "color_blend", "op": {"key": "node-1", "ot": "color.blend", "path": "color", "value": "#ffffff"}}
@@ -1,3 +1,3 @@
1
- {"name": "merge_additive_ops", "ops": [{"key": "cube-1", "otype": "vector3.add", "path": "position", "value": [1, 0, 0]}, {"key": "cube-1", "otype": "vector3.add", "path": "position", "value": [0, 1, 0]}, {"key": "cube-1", "otype": "vector3.add", "path": "position", "value": [0, 0, 1]}], "expected_merged": {"key": "cube-1", "otype": "vector3.add", "path": "position", "value": [1, 1, 1]}}
2
- {"name": "replace_lww_ops", "ops": [{"key": "cube-1", "otype": "number.set", "path": "opacity", "value": 0.3}, {"key": "cube-1", "otype": "number.set", "path": "opacity", "value": 0.7}], "expected_merged": {"key": "cube-1", "otype": "number.set", "path": "opacity", "value": 0.7}}
3
- {"name": "multiple_properties", "ops": [{"key": "cube-1", "otype": "vector3.add", "path": "position", "value": [1, 0, 0]}, {"key": "cube-1", "otype": "number.set", "path": "opacity", "value": 0.5}], "expected_count": 2}
1
+ {"name": "merge_additive_ops", "ops": [{"key": "cube-1", "ot": "vector3.add", "path": "position", "value": [1, 0, 0]}, {"key": "cube-1", "ot": "vector3.add", "path": "position", "value": [0, 1, 0]}, {"key": "cube-1", "ot": "vector3.add", "path": "position", "value": [0, 0, 1]}], "expected_merged": {"key": "cube-1", "ot": "vector3.add", "path": "position", "value": [1, 1, 1]}}
2
+ {"name": "replace_lww_ops", "ops": [{"key": "cube-1", "ot": "number.set", "path": "opacity", "value": 0.3}, {"key": "cube-1", "ot": "number.set", "path": "opacity", "value": 0.7}], "expected_merged": {"key": "cube-1", "ot": "number.set", "path": "opacity", "value": 0.7}}
3
+ {"name": "multiple_properties", "ops": [{"key": "cube-1", "ot": "vector3.add", "path": "position", "value": [1, 0, 0]}, {"key": "cube-1", "ot": "number.set", "path": "opacity", "value": 0.5}], "expected_count": 2}
@@ -1,6 +1,6 @@
1
- {"name": "node_insert_root", "op": {"key": "", "otype": "node.insert", "path": "children", "value": {"key": "scene", "tag": "Scene", "name": "Root Scene"}}}
2
- {"name": "node_insert_mesh", "op": {"key": "scene", "otype": "node.insert", "path": "children", "value": {"key": "cube-1", "tag": "Mesh", "name": "Cube", "position": [0, 0, 0], "scale": [1, 1, 1]}}}
3
- {"name": "node_insert_group", "op": {"key": "scene", "otype": "node.insert", "path": "children", "value": {"key": "group-1", "tag": "Group", "name": "Group"}}}
4
- {"name": "node_insert_nested", "op": {"key": "group-1", "otype": "node.insert", "path": "children", "value": {"key": "sphere-1", "tag": "Mesh", "name": "Sphere"}}}
5
- {"name": "node_remove", "op": {"key": "scene", "otype": "node.remove", "path": "children", "value": "cube-1"}}
6
- {"name": "node_remove_nested", "op": {"key": "group-1", "otype": "node.remove", "path": "children", "value": "sphere-1"}}
1
+ {"name": "node_insert_root", "op": {"key": "", "ot": "node.insert", "path": "children", "value": {"key": "scene", "tag": "Scene", "name": "Root Scene"}}}
2
+ {"name": "node_insert_mesh", "op": {"key": "scene", "ot": "node.insert", "path": "children", "value": {"key": "cube-1", "tag": "Mesh", "name": "Cube", "position": [0, 0, 0], "scale": [1, 1, 1]}}}
3
+ {"name": "node_insert_group", "op": {"key": "scene", "ot": "node.insert", "path": "children", "value": {"key": "group-1", "tag": "Group", "name": "Group"}}}
4
+ {"name": "node_insert_nested", "op": {"key": "group-1", "ot": "node.insert", "path": "children", "value": {"key": "sphere-1", "tag": "Mesh", "name": "Sphere"}}}
5
+ {"name": "node_remove", "op": {"key": "scene", "ot": "node.remove", "path": "children", "value": "cube-1"}}
6
+ {"name": "node_remove_nested", "op": {"key": "group-1", "ot": "node.remove", "path": "children", "value": "sphere-1"}}
@@ -1,7 +1,7 @@
1
- {"name": "number_set", "op": {"key": "node-1", "otype": "number.set", "path": "opacity", "value": 0.5}}
2
- {"name": "number_add", "op": {"key": "node-1", "otype": "number.add", "path": "score", "value": 10}}
3
- {"name": "number_multiply", "op": {"key": "node-1", "otype": "number.multiply", "path": "scale", "value": 2}}
4
- {"name": "number_min", "op": {"key": "node-1", "otype": "number.min", "path": "health", "value": 50}}
5
- {"name": "number_max", "op": {"key": "node-1", "otype": "number.max", "path": "damage", "value": 25}}
6
- {"name": "number_add_negative", "op": {"key": "node-1", "otype": "number.add", "path": "score", "value": -5}}
7
- {"name": "number_multiply_fraction", "op": {"key": "node-1", "otype": "number.multiply", "path": "scale", "value": 0.5}}
1
+ {"name": "number_set", "op": {"key": "node-1", "ot": "number.set", "path": "opacity", "value": 0.5}}
2
+ {"name": "number_add", "op": {"key": "node-1", "ot": "number.add", "path": "score", "value": 10}}
3
+ {"name": "number_multiply", "op": {"key": "node-1", "ot": "number.multiply", "path": "scale", "value": 2}}
4
+ {"name": "number_min", "op": {"key": "node-1", "ot": "number.min", "path": "health", "value": 50}}
5
+ {"name": "number_max", "op": {"key": "node-1", "ot": "number.max", "path": "damage", "value": 25}}
6
+ {"name": "number_add_negative", "op": {"key": "node-1", "ot": "number.add", "path": "score", "value": -5}}
7
+ {"name": "number_multiply_fraction", "op": {"key": "node-1", "ot": "number.multiply", "path": "scale", "value": 0.5}}