@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
@@ -13,42 +13,37 @@ describe('coalesceTextOperations', () => {
13
13
  // Simulate typing "Hello" quickly (within 300ms)
14
14
  const ops: TextOperation[] = [
15
15
  {
16
- otype: 'insert',
16
+ ot: 'insert',
17
17
  id: 'alice:1',
18
- content: 'H',
19
- parentId: null,
18
+ value: [null, 'H'],
20
19
  seq: 1,
21
20
  ts: 1000.0,
22
21
  },
23
22
  {
24
- otype: 'insert',
23
+ ot: 'insert',
25
24
  id: 'alice:2',
26
- content: 'e',
27
- parentId: 'alice:1', // Forms chain with previous
25
+ value: ['alice:1', 'e'], // Forms chain with previous
28
26
  seq: 2,
29
27
  ts: 1000.05, // 50ms later
30
28
  },
31
29
  {
32
- otype: 'insert',
30
+ ot: 'insert',
33
31
  id: 'alice:3',
34
- content: 'l',
35
- parentId: 'alice:2', // Forms chain with previous
32
+ value: ['alice:2', 'l'], // Forms chain with previous
36
33
  seq: 3,
37
34
  ts: 1000.1, // 100ms from start
38
35
  },
39
36
  {
40
- otype: 'insert',
37
+ ot: 'insert',
41
38
  id: 'alice:4',
42
- content: 'l',
43
- parentId: 'alice:3', // Forms chain with previous
39
+ value: ['alice:3', 'l'], // Forms chain with previous
44
40
  seq: 4,
45
41
  ts: 1000.15, // 150ms from start
46
42
  },
47
43
  {
48
- otype: 'insert',
44
+ ot: 'insert',
49
45
  id: 'alice:5',
50
- content: 'o',
51
- parentId: 'alice:4', // Forms chain with previous
46
+ value: ['alice:4', 'o'], // Forms chain with previous
52
47
  seq: 5,
53
48
  ts: 1000.2, // 200ms from start
54
49
  },
@@ -56,54 +51,49 @@ describe('coalesceTextOperations', () => {
56
51
 
57
52
  const result = coalesceTextOperations(ops, { thresholdMs: 300 });
58
53
 
59
- // Should coalesce into ONE operation with content="Hello"
54
+ // Should coalesce into ONE operation with value="Hello"
60
55
  expect(result).toHaveLength(1);
61
- expect(result[0].otype).toBe('insert');
62
- expect((result[0] as any).content).toBe('Hello');
56
+ expect(result[0].ot).toBe('insert');
57
+ expect((result[0] as any).value[1]).toBe('Hello');
63
58
  expect((result[0] as any).id).toBe('alice:1'); // Keep first ID
64
- expect((result[0] as any).parentId).toBe(null); // Keep first parentId
59
+ expect((result[0] as any).value[0]).toBeNull(); // Keep first anchor (null)
65
60
  });
66
61
 
67
62
  it('should respect time threshold and create separate operations for slow typing', () => {
68
63
  // Simulate typing "He" quickly, then pause, then "llo" quickly
69
64
  const ops: TextOperation[] = [
70
65
  {
71
- otype: 'insert',
66
+ ot: 'insert',
72
67
  id: 'alice:1',
73
- content: 'H',
74
- parentId: null,
68
+ value: [null, 'H'],
75
69
  seq: 1,
76
70
  ts: 1000.0,
77
71
  },
78
72
  {
79
- otype: 'insert',
73
+ ot: 'insert',
80
74
  id: 'alice:2',
81
- content: 'e',
82
- parentId: 'alice:1',
75
+ value: ['alice:1', 'e'],
83
76
  seq: 2,
84
77
  ts: 1000.05, // 50ms later - within threshold
85
78
  },
86
79
  {
87
- otype: 'insert',
80
+ ot: 'insert',
88
81
  id: 'alice:3',
89
- content: 'l',
90
- parentId: 'alice:2',
82
+ value: ['alice:2', 'l'],
91
83
  seq: 3,
92
84
  ts: 1000.5, // 450ms later - EXCEEDS threshold
93
85
  },
94
86
  {
95
- otype: 'insert',
87
+ ot: 'insert',
96
88
  id: 'alice:4',
97
- content: 'l',
98
- parentId: 'alice:3',
89
+ value: ['alice:3', 'l'],
99
90
  seq: 4,
100
91
  ts: 1000.55, // 50ms after 'l'
101
92
  },
102
93
  {
103
- otype: 'insert',
94
+ ot: 'insert',
104
95
  id: 'alice:5',
105
- content: 'o',
106
- parentId: 'alice:4',
96
+ value: ['alice:4', 'o'],
107
97
  seq: 5,
108
98
  ts: 1000.6, // 50ms after second 'l'
109
99
  },
@@ -113,35 +103,32 @@ describe('coalesceTextOperations', () => {
113
103
 
114
104
  // Should create TWO operations: "He" and "llo"
115
105
  expect(result).toHaveLength(2);
116
- expect((result[0] as any).content).toBe('He');
106
+ expect((result[0] as any).value[1]).toBe('He');
117
107
  expect((result[0] as any).id).toBe('alice:1');
118
- expect((result[1] as any).content).toBe('llo');
108
+ expect((result[1] as any).value[1]).toBe('llo');
119
109
  expect((result[1] as any).id).toBe('alice:3');
120
110
  });
121
111
 
122
112
  it('should handle single character operations (no coalescence)', () => {
123
113
  const ops: TextOperation[] = [
124
114
  {
125
- otype: 'insert',
115
+ ot: 'insert',
126
116
  id: 'alice:1',
127
- content: 'H',
128
- parentId: null,
117
+ value: [null, 'H'],
129
118
  seq: 1,
130
119
  ts: 1000.0,
131
120
  },
132
121
  {
133
- otype: 'insert',
122
+ ot: 'insert',
134
123
  id: 'alice:2',
135
- content: 'e',
136
- parentId: 'alice:1',
124
+ value: ['alice:1', 'e'],
137
125
  seq: 2,
138
126
  ts: 1001.5, // 1500ms later - exceeds threshold
139
127
  },
140
128
  {
141
- otype: 'insert',
129
+ ot: 'insert',
142
130
  id: 'alice:3',
143
- content: 'l',
144
- parentId: 'alice:2',
131
+ value: ['alice:2', 'l'],
145
132
  seq: 3,
146
133
  ts: 1003.0, // 1500ms later - exceeds threshold
147
134
  },
@@ -151,26 +138,24 @@ describe('coalesceTextOperations', () => {
151
138
 
152
139
  // Should create THREE separate operations (no coalescence)
153
140
  expect(result).toHaveLength(3);
154
- expect((result[0] as any).content).toBe('H');
155
- expect((result[1] as any).content).toBe('e');
156
- expect((result[2] as any).content).toBe('l');
141
+ expect((result[0] as any).value[1]).toBe('H');
142
+ expect((result[1] as any).value[1]).toBe('e');
143
+ expect((result[2] as any).value[1]).toBe('l');
157
144
  });
158
145
 
159
146
  it('should handle non-sequential IDs (different agents)', () => {
160
147
  const ops: TextOperation[] = [
161
148
  {
162
- otype: 'insert',
149
+ ot: 'insert',
163
150
  id: 'alice:1',
164
- content: 'H',
165
- parentId: null,
151
+ value: [null, 'H'],
166
152
  seq: 1,
167
153
  ts: 1000.0,
168
154
  },
169
155
  {
170
- otype: 'insert',
156
+ ot: 'insert',
171
157
  id: 'bob:1', // Different agent
172
- content: 'e',
173
- parentId: 'alice:1',
158
+ value: ['alice:1', 'e'],
174
159
  seq: 2,
175
160
  ts: 1000.05,
176
161
  },
@@ -180,33 +165,29 @@ describe('coalesceTextOperations', () => {
180
165
 
181
166
  // Should NOT coalesce (different agents)
182
167
  expect(result).toHaveLength(2);
183
- expect((result[0] as any).content).toBe('H');
184
- expect((result[1] as any).content).toBe('e');
168
+ expect((result[0] as any).value[1]).toBe('H');
169
+ expect((result[1] as any).value[1]).toBe('e');
185
170
  });
186
171
 
187
172
  it('should handle delete operations (flush pending inserts)', () => {
188
173
  const ops: TextOperation[] = [
189
174
  {
190
- otype: 'insert',
175
+ ot: 'insert',
191
176
  id: 'alice:1',
192
- content: 'H',
193
- parentId: null,
177
+ value: [null, 'H'],
194
178
  seq: 1,
195
179
  ts: 1000.0,
196
180
  },
197
181
  {
198
- otype: 'insert',
182
+ ot: 'insert',
199
183
  id: 'alice:2',
200
- content: 'e',
201
- parentId: 'alice:1',
184
+ value: ['alice:1', 'e'],
202
185
  seq: 2,
203
186
  ts: 1000.05,
204
187
  },
205
188
  {
206
- otype: 'delete',
207
- deletions: [{ id: 'alice:2', length: 1 }],
208
- seq: 3,
209
- ts: 1000.1,
189
+ ot: 'delete',
190
+ rm: [['alice:2', 1]],
210
191
  } as any,
211
192
  ];
212
193
 
@@ -214,34 +195,31 @@ describe('coalesceTextOperations', () => {
214
195
 
215
196
  // Should flush "He" and then add the delete
216
197
  expect(result).toHaveLength(2);
217
- expect(result[0].otype).toBe('insert');
218
- expect((result[0] as any).content).toBe('He');
219
- expect(result[1].otype).toBe('delete');
198
+ expect(result[0].ot).toBe('insert');
199
+ expect((result[0] as any).value[1]).toBe('He');
200
+ expect(result[1].ot).toBe('delete');
220
201
  });
221
202
 
222
203
  it('should verify _lastCharId metadata is tracked correctly', () => {
223
204
  const ops: TextOperation[] = [
224
205
  {
225
- otype: 'insert',
206
+ ot: 'insert',
226
207
  id: 'alice:1',
227
- content: 'a',
228
- parentId: null,
208
+ value: [null, 'a'],
229
209
  seq: 1,
230
210
  ts: 1000.0,
231
211
  },
232
212
  {
233
- otype: 'insert',
213
+ ot: 'insert',
234
214
  id: 'alice:2',
235
- content: 'b',
236
- parentId: 'alice:1',
215
+ value: ['alice:1', 'b'],
237
216
  seq: 2,
238
217
  ts: 1000.05,
239
218
  },
240
219
  {
241
- otype: 'insert',
220
+ ot: 'insert',
242
221
  id: 'alice:3',
243
- content: 'c',
244
- parentId: 'alice:2', // This should match _lastCharId after first merge
222
+ value: ['alice:2', 'c'], // This should match _lastCharId after first merge
245
223
  seq: 3,
246
224
  ts: 1000.1,
247
225
  },
@@ -251,7 +229,7 @@ describe('coalesceTextOperations', () => {
251
229
 
252
230
  // Should coalesce all three
253
231
  expect(result).toHaveLength(1);
254
- expect((result[0] as any).content).toBe('abc');
232
+ expect((result[0] as any).value[1]).toBe('abc');
255
233
  expect((result[0] as any)._lastCharId).toBe('alice:3');
256
234
  });
257
235
  });
@@ -261,32 +239,32 @@ describe('coalesceTextOperations', () => {
261
239
  // Simulate pressing backspace 7 times (7 separate delete operations)
262
240
  const ops: TextOperation[] = [
263
241
  {
264
- otype: 'delete',
265
- deletions: [{ id: 'alice:7', length: 1 }],
242
+ ot: 'delete',
243
+ rm: [['alice:7', 1]],
266
244
  } as any,
267
245
  {
268
- otype: 'delete',
269
- deletions: [{ id: 'alice:6', length: 1 }],
246
+ ot: 'delete',
247
+ rm: [['alice:6', 1]],
270
248
  } as any,
271
249
  {
272
- otype: 'delete',
273
- deletions: [{ id: 'alice:5', length: 1 }],
250
+ ot: 'delete',
251
+ rm: [['alice:5', 1]],
274
252
  } as any,
275
253
  {
276
- otype: 'delete',
277
- deletions: [{ id: 'alice:4', length: 1 }],
254
+ ot: 'delete',
255
+ rm: [['alice:4', 1]],
278
256
  } as any,
279
257
  {
280
- otype: 'delete',
281
- deletions: [{ id: 'alice:3', length: 1 }],
258
+ ot: 'delete',
259
+ rm: [['alice:3', 1]],
282
260
  } as any,
283
261
  {
284
- otype: 'delete',
285
- deletions: [{ id: 'alice:2', length: 1 }],
262
+ ot: 'delete',
263
+ rm: [['alice:2', 1]],
286
264
  } as any,
287
265
  {
288
- otype: 'delete',
289
- deletions: [{ id: 'alice:1', length: 1 }],
266
+ ot: 'delete',
267
+ rm: [['alice:1', 1]],
290
268
  } as any,
291
269
  ];
292
270
 
@@ -294,30 +272,30 @@ describe('coalesceTextOperations', () => {
294
272
 
295
273
  // Should merge into ONE delete operation
296
274
  expect(result).toHaveLength(1);
297
- expect(result[0].otype).toBe('delete');
275
+ expect(result[0].ot).toBe('delete');
298
276
 
299
277
  // CRITICAL: Deletions array should be optimized (7 entries → 1 entry)
300
278
  const deleteOp = result[0] as any;
301
- expect(deleteOp.deletions).toBeDefined();
302
- expect(deleteOp.deletions).toHaveLength(1);
303
- expect(deleteOp.deletions[0]).toEqual({ id: 'alice:1', length: 7 });
279
+ expect(deleteOp.rm).toBeDefined();
280
+ expect(deleteOp.rm).toHaveLength(1);
281
+ expect(deleteOp.rm[0]).toEqual(['alice:1', 7]);
304
282
  });
305
283
 
306
284
  it('should optimize deletions with gaps', () => {
307
285
  // Deletions with non-consecutive IDs (can't be fully merged)
308
286
  const ops: TextOperation[] = [
309
287
  {
310
- otype: 'delete',
311
- deletions: [
312
- { id: 'alice:5', length: 1 },
313
- { id: 'alice:4', length: 1 },
288
+ ot: 'delete',
289
+ rm: [
290
+ ['alice:5', 1],
291
+ ['alice:4', 1],
314
292
  ],
315
293
  } as any,
316
294
  {
317
- otype: 'delete',
318
- deletions: [
319
- { id: 'alice:2', length: 1 }, // Gap! alice:3 is missing
320
- { id: 'alice:1', length: 1 },
295
+ ot: 'delete',
296
+ rm: [
297
+ ['alice:2', 1], // Gap! alice:3 is missing
298
+ ['alice:1', 1],
321
299
  ],
322
300
  } as any,
323
301
  ];
@@ -326,14 +304,14 @@ describe('coalesceTextOperations', () => {
326
304
 
327
305
  // Should merge into ONE delete operation
328
306
  expect(result).toHaveLength(1);
329
- expect(result[0].otype).toBe('delete');
307
+ expect(result[0].ot).toBe('delete');
330
308
 
331
309
  // Deletions should be optimized: [alice:4-5 (len 2), alice:1-2 (len 2)]
332
310
  const deleteOp = result[0] as any;
333
- expect(deleteOp.deletions).toBeDefined();
334
- expect(deleteOp.deletions).toHaveLength(2); // Two ranges due to gap
335
- expect(deleteOp.deletions).toContainEqual({ id: 'alice:4', length: 2 });
336
- expect(deleteOp.deletions).toContainEqual({ id: 'alice:1', length: 2 });
311
+ expect(deleteOp.rm).toBeDefined();
312
+ expect(deleteOp.rm).toHaveLength(2); // Two ranges due to gap
313
+ expect(deleteOp.rm).toContainEqual(['alice:4', 2]);
314
+ expect(deleteOp.rm).toContainEqual(['alice:1', 2]);
337
315
  });
338
316
  });
339
317
 
@@ -346,10 +324,9 @@ describe('coalesceTextOperations', () => {
346
324
  it('should handle single operation', () => {
347
325
  const ops: TextOperation[] = [
348
326
  {
349
- otype: 'insert',
327
+ ot: 'insert',
350
328
  id: 'alice:1',
351
- content: 'H',
352
- parentId: null,
329
+ value: [null, 'H'],
353
330
  seq: 1,
354
331
  ts: 1000.0,
355
332
  },
@@ -357,7 +334,7 @@ describe('coalesceTextOperations', () => {
357
334
 
358
335
  const result = coalesceTextOperations(ops);
359
336
  expect(result).toHaveLength(1);
360
- expect((result[0] as any).content).toBe('H');
337
+ expect((result[0] as any).value[1]).toBe('H');
361
338
  });
362
339
  });
363
340
  });
@@ -32,17 +32,17 @@ import type { CRDTMessage, Operation, SceneGraph } from '../../src/operations/Op
32
32
 
33
33
  function makeRemoteMsg(
34
34
  id: string,
35
- sessionId: string,
35
+ client: string,
36
36
  ops: Operation[],
37
- lamportTime: number,
37
+ lt: number,
38
38
  clock?: Record<string, number>,
39
39
  ): CRDTMessage {
40
40
  return {
41
41
  id,
42
- sessionId,
43
- clock: clock ?? { [sessionId]: lamportTime },
44
- lamportTime,
45
- timestamp: Date.now() / 1000,
42
+ client,
43
+ clock: clock ?? { [client]: lt },
44
+ lt,
45
+ ts: Date.now() / 1000,
46
46
  ops,
47
47
  };
48
48
  }
@@ -50,31 +50,31 @@ function makeRemoteMsg(
50
50
  function nodeInsertOp(parentKey: string, nodeKey: string, props: Record<string, unknown> = {}): Operation {
51
51
  return {
52
52
  key: parentKey,
53
- otype: 'node.insert',
53
+ ot: 'node.insert',
54
54
  path: 'children',
55
55
  value: { key: nodeKey, tag: 'Mesh', name: nodeKey, ...props },
56
56
  } as Operation;
57
57
  }
58
58
 
59
59
  function vec3SetOp(nodeKey: string, path: string, value: [number, number, number]): Operation {
60
- return { key: nodeKey, otype: 'vector3.set', path, value } as Operation;
60
+ return { key: nodeKey, ot: 'vector3.set', path, value } as Operation;
61
61
  }
62
62
 
63
63
  function numSetOp(nodeKey: string, path: string, value: number): Operation {
64
- return { key: nodeKey, otype: 'number.set', path, value } as Operation;
64
+ return { key: nodeKey, ot: 'number.set', path, value } as Operation;
65
65
  }
66
66
 
67
67
  function numAddOp(nodeKey: string, path: string, value: number): Operation {
68
- return { key: nodeKey, otype: 'number.add', path, value } as Operation;
68
+ return { key: nodeKey, ot: 'number.add', path, value } as Operation;
69
69
  }
70
70
 
71
71
  function nodeRemoveOp(parentKey: string, nodeKey: string): Operation {
72
- return { key: parentKey, otype: 'node.remove', path: 'children', value: nodeKey } as Operation;
72
+ return { key: parentKey, ot: 'node.remove', path: 'children', value: nodeKey } as Operation;
73
73
  }
74
74
 
75
75
  /** Set up a state with a scene root + cube node */
76
- function setupState(sessionId = 'test-session'): ClientState {
77
- let s = createInitialState(sessionId);
76
+ function setupState(client = 'test-session'): ClientState {
77
+ let s = createInitialState(client);
78
78
  s = onEdit(s, nodeInsertOp('', 'scene', { tag: 'Scene' }));
79
79
  const { state: s1, msg: m1 } = commitEdits(s);
80
80
  s = onServerAck(s1, m1!.id);
@@ -214,12 +214,12 @@ describe('Client compact() (Issue #50)', () => {
214
214
  s = commitAndAck(s, vec3SetOp('cube', 'position', [1, 2, 3]));
215
215
  s = commitAndAck(s, numSetOp('cube', 'opacity', 0.5));
216
216
 
217
- const lamportBefore = s.snapshot.lamportTime;
217
+ const lamportBefore = s.snapshot.lt;
218
218
  const indexBefore = s.snapshot.journalIndex;
219
219
 
220
220
  s = compact(s);
221
221
 
222
- expect(s.snapshot.lamportTime).toBeGreaterThan(lamportBefore);
222
+ expect(s.snapshot.lt).toBeGreaterThan(lamportBefore);
223
223
  expect(s.snapshot.journalIndex).toBeGreaterThan(indexBefore);
224
224
  });
225
225
 
@@ -346,7 +346,7 @@ describe('initFromServer (state transfer)', () => {
346
346
  const emptySnapshot = {
347
347
  graph: createEmptyGraph(),
348
348
  vectorClock: {},
349
- lamportTime: 0,
349
+ lt: 0,
350
350
  journalIndex: 0,
351
351
  };
352
352
 
@@ -409,7 +409,7 @@ describe('initFromServer (state transfer)', () => {
409
409
  const emptySnapshot = {
410
410
  graph: createEmptyGraph(),
411
411
  vectorClock: {},
412
- lamportTime: 0,
412
+ lt: 0,
413
413
  journalIndex: 0,
414
414
  };
415
415
 
@@ -423,7 +423,7 @@ describe('initFromServer (state transfer)', () => {
423
423
  // new-client's clock should include alice and bob
424
424
  expect(state.vectorClock['alice']).toBeGreaterThanOrEqual(2);
425
425
  expect(state.vectorClock['bob']).toBeGreaterThanOrEqual(1);
426
- expect(state.lamportTime).toBeGreaterThanOrEqual(3);
426
+ expect(state.lt).toBeGreaterThanOrEqual(3);
427
427
  });
428
428
  });
429
429