@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
@@ -12,7 +12,7 @@ import type { OpMeta } from './apply/types.js';
12
12
  import * as registry from './apply/index.js';
13
13
 
14
14
  /**
15
- * Handler map: otype -> apply function
15
+ * Handler map: ot -> apply function
16
16
  */
17
17
  const handlers: Record<string, (graph: SceneGraph, op: Operation, meta: OpMeta) => void> = {
18
18
  // Number operations
@@ -80,14 +80,50 @@ export function applyOperation(
80
80
  op: Operation,
81
81
  meta: OpMeta
82
82
  ): void {
83
- const handler = handlers[op.otype];
83
+ const handler = handlers[op.ot];
84
84
  if (handler) {
85
85
  handler(graph, op, meta);
86
86
  } else {
87
- console.warn(`Unknown otype: ${op.otype}`);
87
+ console.warn(`Unknown ot: ${op.ot}`);
88
88
  }
89
89
  }
90
90
 
91
+ /**
92
+ * Compare operations for sorting by Lamport timestamp.
93
+ * CRDT invariant: operations must be applied in causal order (seq → ts → id).
94
+ *
95
+ * This ensures that:
96
+ * 1. Operations with lower Lamport clocks are applied first
97
+ * 2. If Lamport clocks are equal, wall-clock time breaks the tie
98
+ * 3. If both are equal, lexicographic ID order ensures determinism
99
+ *
100
+ * @param a - First operation
101
+ * @param b - Second operation
102
+ * @returns Negative if a < b, positive if a > b, 0 if equal
103
+ */
104
+ function compareOperations(a: Operation, b: Operation): number {
105
+ const aOp = a as any;
106
+ const bOp = b as any;
107
+
108
+ // Compare by Lamport clock (seq)
109
+ if (aOp.seq !== undefined && bOp.seq !== undefined) {
110
+ if (aOp.seq !== bOp.seq) return aOp.seq - bOp.seq;
111
+ }
112
+
113
+ // If equal or missing, compare by wall-clock time (ts)
114
+ if (aOp.ts !== undefined && bOp.ts !== undefined) {
115
+ if (aOp.ts !== bOp.ts) return aOp.ts - bOp.ts;
116
+ }
117
+
118
+ // Final fallback: compare by ID (lexicographic)
119
+ if (aOp.id && bOp.id) {
120
+ return String(aOp.id).localeCompare(String(bOp.id));
121
+ }
122
+
123
+ // If no metadata, preserve original order
124
+ return 0;
125
+ }
126
+
91
127
  /**
92
128
  * Shallow clone the graph and modified nodes
93
129
  */
@@ -112,24 +148,29 @@ export function applyMessage(graph: SceneGraph, msg: CRDTMessage): SceneGraph {
112
148
  const newGraph = shallowCloneGraph(graph);
113
149
 
114
150
  const meta: OpMeta = {
115
- sessionId: msg.sessionId,
151
+ client: msg.client,
116
152
  clock: msg.clock,
117
- lamportTime: msg.lamportTime,
118
- timestamp: msg.timestamp,
153
+ lt: msg.lt,
154
+ ts: msg.ts,
119
155
  };
120
156
 
121
- for (const op of msg.ops) {
157
+ // Sort operations by Lamport timestamp to ensure causal order
158
+ // This is critical for CRDT correctness, especially for text operations
159
+ // where replace ops may reference IDs from insert ops
160
+ const sortedOps = [...msg.ops].sort(compareOperations);
161
+
162
+ for (const op of sortedOps) {
122
163
  // Determine all node keys that this operation will mutate
123
164
  const keysToClone: string[] = [];
124
165
  if (op.key && newGraph.nodes[op.key]) {
125
166
  keysToClone.push(op.key);
126
167
  }
127
- if (op.otype === 'node.move') {
168
+ if (op.ot === 'node.move') {
128
169
  const { nodeKey, newParent } = (op as any).value;
129
170
  if (nodeKey && newGraph.nodes[nodeKey]) keysToClone.push(nodeKey);
130
171
  if (newParent && newGraph.nodes[newParent]) keysToClone.push(newParent);
131
172
  }
132
- if (op.otype === 'node.remove') {
173
+ if (op.ot === 'node.remove') {
133
174
  const nodeKey = (op as any).value;
134
175
  if (typeof nodeKey === 'string' && newGraph.nodes[nodeKey]) keysToClone.push(nodeKey);
135
176
  }
@@ -158,13 +199,16 @@ export function applyMessage(graph: SceneGraph, msg: CRDTMessage): SceneGraph {
158
199
  */
159
200
  export function applyMessageMut(graph: SceneGraph, msg: CRDTMessage): void {
160
201
  const meta: OpMeta = {
161
- sessionId: msg.sessionId,
202
+ client: msg.client,
162
203
  clock: msg.clock,
163
- lamportTime: msg.lamportTime,
164
- timestamp: msg.timestamp,
204
+ lt: msg.lt,
205
+ ts: msg.ts,
165
206
  };
166
207
 
167
- for (const op of msg.ops) {
208
+ // Sort operations by Lamport timestamp to ensure causal order
209
+ const sortedOps = [...msg.ops].sort(compareOperations);
210
+
211
+ for (const op of sortedOps) {
168
212
  applyOperation(graph, op, meta);
169
213
  }
170
214
  }
package/src/serdes.ts CHANGED
@@ -22,7 +22,7 @@ export type WireMessage =
22
22
  | { mtype: 'error'; msgId: string; error: string }
23
23
  | { mtype: 'state'; snapshot: Snapshot; journal: CRDTMessage[] }
24
24
  | { mtype: 'sync'; vectorClock?: VectorClock; filter: Uint8Array; count: number }
25
- | { mtype: 'heartbeat'; sessionId: string; vectorClock: VectorClock }
25
+ | { mtype: 'heartbeat'; client: string; vectorClock: VectorClock }
26
26
  | { mtype: 'room-reset' }
27
27
  | { mtype: 'compaction-watermark'; watermark: VectorClock };
28
28
 
@@ -2,29 +2,29 @@
2
2
  * ConflictResolver - Operation-based property conflict resolution
3
3
  *
4
4
  * Merges concurrent property updates based on:
5
- * - Operation type (otype) which encodes dtype.operation
5
+ * - Operation type (ot) which encodes dtype.operation
6
6
  * - DType merge functions
7
7
  *
8
- * Schema-less: dtype and operation are derived from otype.
8
+ * Schema-less: dtype and operation are derived from ot.
9
9
  */
10
10
 
11
11
  import { DType, type ValueWithMeta } from './DType.js';
12
12
 
13
13
  /**
14
- * Parse otype string into dtype and operation
14
+ * Parse ot string into dtype and operation
15
15
  *
16
16
  * @example
17
17
  * parseOtype('vector3.add') // { dtype: 'vector3', operation: 'add' }
18
18
  * parseOtype('number.set') // { dtype: 'number', operation: 'set' }
19
19
  */
20
- export function parseOtype(otype: string): { dtype: string; operation: string } {
21
- const dotIndex = otype.indexOf('.');
20
+ export function parseOtype(ot: string): { dtype: string; operation: string } {
21
+ const dotIndex = ot.indexOf('.');
22
22
  if (dotIndex === -1) {
23
- return { dtype: otype, operation: 'set' };
23
+ return { dtype: ot, operation: 'set' };
24
24
  }
25
25
  return {
26
- dtype: otype.slice(0, dotIndex),
27
- operation: otype.slice(dotIndex + 1),
26
+ dtype: ot.slice(0, dotIndex),
27
+ operation: ot.slice(dotIndex + 1),
28
28
  };
29
29
  }
30
30
 
@@ -32,23 +32,23 @@ export class ConflictResolver {
32
32
  /**
33
33
  * Merge a single property from multiple concurrent updates
34
34
  *
35
- * @param otype - Operation type (e.g., 'vector3.add', 'number.set')
35
+ * @param ot - Operation type (e.g., 'vector3.add', 'number.set')
36
36
  * @param values - Array of values with metadata
37
37
  * @returns Merged value
38
38
  */
39
39
  mergeProperty<T = any>(
40
- otype: string,
40
+ ot: string,
41
41
  values: ValueWithMeta<T>[]
42
42
  ): T {
43
43
  if (values.length === 0) {
44
- throw new Error(`Cannot merge empty values for otype: ${otype}`);
44
+ throw new Error(`Cannot merge empty values for ot: ${ot}`);
45
45
  }
46
46
 
47
47
  if (values.length === 1) {
48
48
  return values[0].value;
49
49
  }
50
50
 
51
- const { dtype, operation } = parseOtype(otype);
51
+ const { dtype, operation } = parseOtype(ot);
52
52
 
53
53
  // Get merge function based on dtype and operation
54
54
  const mergeFn = this.getMergeFunction(dtype, operation);
@@ -72,14 +72,14 @@ export class ConflictResolver {
72
72
  mergeProperties(
73
73
  updates: Array<{
74
74
  properties: Record<string, any>;
75
- otypes: Record<string, string>; // otype per property
76
- lamportTime: number;
77
- sessionId: string;
75
+ ots: Record<string, string>; // ot per property
76
+ lt: number;
77
+ client: string;
78
78
  }>
79
79
  ): Record<string, any> {
80
80
  // Group values by property name
81
81
  const valuesByProperty = new Map<string, ValueWithMeta[]>();
82
- const otypeByProperty = new Map<string, string>();
82
+ const otByProperty = new Map<string, string>();
83
83
 
84
84
  for (const update of updates) {
85
85
  for (const [key, value] of Object.entries(update.properties)) {
@@ -89,13 +89,13 @@ export class ConflictResolver {
89
89
 
90
90
  valuesByProperty.get(key)!.push({
91
91
  value,
92
- lamportTime: update.lamportTime,
93
- sessionId: update.sessionId,
92
+ lt: update.lt,
93
+ client: update.client,
94
94
  });
95
95
 
96
- // Store otype for this property
97
- if (update.otypes?.[key]) {
98
- otypeByProperty.set(key, update.otypes[key]);
96
+ // Store ot for this property
97
+ if (update.ots?.[key]) {
98
+ otByProperty.set(key, update.ots[key]);
99
99
  }
100
100
  }
101
101
  }
@@ -104,8 +104,8 @@ export class ConflictResolver {
104
104
  const result: Record<string, any> = {};
105
105
 
106
106
  for (const [propertyName, values] of valuesByProperty.entries()) {
107
- const otype = otypeByProperty.get(propertyName) || 'any.set';
108
- result[propertyName] = this.mergeProperty(otype, values);
107
+ const ot = otByProperty.get(propertyName) || 'any.set';
108
+ result[propertyName] = this.mergeProperty(ot, values);
109
109
  }
110
110
 
111
111
  return result;
@@ -12,8 +12,8 @@
12
12
  */
13
13
  export interface ValueWithMeta<T = any> {
14
14
  value: T;
15
- lamportTime: number;
16
- sessionId?: string;
15
+ lt: number;
16
+ client?: string;
17
17
  }
18
18
 
19
19
  /**
@@ -37,7 +37,7 @@ export const DType = {
37
37
  * SET - Last-Write-Wins
38
38
  */
39
39
  set: ((values: ValueWithMeta<number>[]) => {
40
- const sorted = [...values].sort((a, b) => b.lamportTime - a.lamportTime);
40
+ const sorted = [...values].sort((a, b) => b.lt - a.lt);
41
41
  return sorted[0].value;
42
42
  }) as MergeFn<number>,
43
43
 
@@ -78,7 +78,7 @@ export const DType = {
78
78
  * SET - Last-Write-Wins
79
79
  */
80
80
  set: ((values: ValueWithMeta<string>[]) => {
81
- const sorted = [...values].sort((a, b) => b.lamportTime - a.lamportTime);
81
+ const sorted = [...values].sort((a, b) => b.lt - a.lt);
82
82
  return sorted[0].value;
83
83
  }) as MergeFn<string>,
84
84
 
@@ -86,7 +86,7 @@ export const DType = {
86
86
  * CONCAT - Concatenate (ordered by lamport)
87
87
  */
88
88
  concat: ((values: ValueWithMeta<string>[], separator = '\n') => {
89
- const sorted = [...values].sort((a, b) => a.lamportTime - b.lamportTime);
89
+ const sorted = [...values].sort((a, b) => a.lt - b.lt);
90
90
  return sorted.map((v) => v.value).join(separator);
91
91
  }) as MergeFn<string>,
92
92
  },
@@ -99,7 +99,7 @@ export const DType = {
99
99
  * SET - Last-Write-Wins
100
100
  */
101
101
  set: ((values: ValueWithMeta<boolean>[]) => {
102
- const sorted = [...values].sort((a, b) => b.lamportTime - a.lamportTime);
102
+ const sorted = [...values].sort((a, b) => b.lt - a.lt);
103
103
  return sorted[0].value;
104
104
  }) as MergeFn<boolean>,
105
105
 
@@ -130,7 +130,7 @@ export const DType = {
130
130
  * SET - Replace entire vector (preserves user intent)
131
131
  */
132
132
  set: ((values: ValueWithMeta<[number, number, number]>[]) => {
133
- const sorted = [...values].sort((a, b) => b.lamportTime - a.lamportTime);
133
+ const sorted = [...values].sort((a, b) => b.lt - a.lt);
134
134
  return sorted[0].value;
135
135
  }) as MergeFn<[number, number, number]>,
136
136
 
@@ -169,7 +169,7 @@ export const DType = {
169
169
  * SET - Replace entire quaternion
170
170
  */
171
171
  set: ((values: ValueWithMeta<[number, number, number, number]>[]) => {
172
- const sorted = [...values].sort((a, b) => b.lamportTime - a.lamportTime);
172
+ const sorted = [...values].sort((a, b) => b.lt - a.lt);
173
173
  return sorted[0].value;
174
174
  }) as MergeFn<[number, number, number, number]>,
175
175
 
@@ -177,7 +177,7 @@ export const DType = {
177
177
  * MULTIPLY - Quaternion multiplication (composition)
178
178
  */
179
179
  multiply: ((values: ValueWithMeta<[number, number, number, number]>[]) => {
180
- const sorted = [...values].sort((a, b) => a.lamportTime - b.lamportTime);
180
+ const sorted = [...values].sort((a, b) => a.lt - b.lt);
181
181
  let result = sorted[0].value;
182
182
 
183
183
  for (let i = 1; i < sorted.length; i++) {
@@ -196,7 +196,7 @@ export const DType = {
196
196
  * SET - Replace color
197
197
  */
198
198
  set: ((values: ValueWithMeta<string>[]) => {
199
- const sorted = [...values].sort((a, b) => b.lamportTime - a.lamportTime);
199
+ const sorted = [...values].sort((a, b) => b.lt - a.lt);
200
200
  return sorted[0].value;
201
201
  }) as MergeFn<string>,
202
202
 
@@ -224,7 +224,7 @@ export const DType = {
224
224
  * SET - Replace entire array
225
225
  */
226
226
  set: ((values: ValueWithMeta<any[]>[]) => {
227
- const sorted = [...values].sort((a, b) => b.lamportTime - a.lamportTime);
227
+ const sorted = [...values].sort((a, b) => b.lt - a.lt);
228
228
  return sorted[0].value;
229
229
  }) as MergeFn<any[]>,
230
230
 
@@ -240,7 +240,7 @@ export const DType = {
240
240
  * APPEND - Append all (ordered by lamport)
241
241
  */
242
242
  append: ((values: ValueWithMeta<any[]>[]) => {
243
- const sorted = [...values].sort((a, b) => a.lamportTime - b.lamportTime);
243
+ const sorted = [...values].sort((a, b) => a.lt - b.lt);
244
244
  return sorted.flatMap((v) => v.value);
245
245
  }) as MergeFn<any[]>,
246
246
  },
@@ -253,7 +253,7 @@ export const DType = {
253
253
  * SET - Replace entire object
254
254
  */
255
255
  set: ((values: ValueWithMeta<Record<string, any>>[]) => {
256
- const sorted = [...values].sort((a, b) => b.lamportTime - a.lamportTime);
256
+ const sorted = [...values].sort((a, b) => b.lt - a.lt);
257
257
  return sorted[0].value;
258
258
  }) as MergeFn<Record<string, any>>,
259
259
 
@@ -266,9 +266,9 @@ export const DType = {
266
266
 
267
267
  for (const v of values) {
268
268
  for (const [key, value] of Object.entries(v.value)) {
269
- if (!latestTimes[key] || v.lamportTime > latestTimes[key]) {
269
+ if (!latestTimes[key] || v.lt > latestTimes[key]) {
270
270
  result[key] = value;
271
- latestTimes[key] = v.lamportTime;
271
+ latestTimes[key] = v.lt;
272
272
  }
273
273
  }
274
274
  }
@@ -287,7 +287,7 @@ export const DType = {
287
287
  immutable: {
288
288
  set: ((values: ValueWithMeta[]) => {
289
289
  // Return first value (creation value)
290
- const sorted = [...values].sort((a, b) => a.lamportTime - b.lamportTime);
290
+ const sorted = [...values].sort((a, b) => a.lt - b.lt);
291
291
  return sorted[0].value;
292
292
  }) as MergeFn,
293
293
  },
@@ -11,35 +11,35 @@ export type VectorClock = Record<string, number>;
11
11
 
12
12
  export class VectorClockManager {
13
13
  /**
14
- * Create a new vector clock for a session
15
- * Initializes the session's counter to 0
14
+ * Create a new vector clock for a client
15
+ * Initializes the client's counter to 0
16
16
  */
17
- create(sessionId: string): VectorClock {
18
- return { [sessionId]: 0 };
17
+ create(client: string): VectorClock {
18
+ return { [client]: 0 };
19
19
  }
20
20
 
21
21
  /**
22
- * Increment the counter for a session
22
+ * Increment the counter for a client
23
23
  * Returns a new clock (immutable)
24
24
  */
25
- increment(clock: VectorClock, sessionId: string): VectorClock {
26
- const currentValue = clock[sessionId] || 0;
25
+ increment(clock: VectorClock, client: string): VectorClock {
26
+ const currentValue = clock[client] || 0;
27
27
  return {
28
28
  ...clock,
29
- [sessionId]: currentValue + 1,
29
+ [client]: currentValue + 1,
30
30
  };
31
31
  }
32
32
 
33
33
  /**
34
34
  * Merge two vector clocks
35
- * Takes the maximum value for each session
35
+ * Takes the maximum value for each client
36
36
  * Used when receiving remote operations
37
37
  */
38
38
  merge(clock1: VectorClock, clock2: VectorClock): VectorClock {
39
39
  const merged: VectorClock = { ...clock1 };
40
40
 
41
- Object.entries(clock2).forEach(([sessionId, count]) => {
42
- merged[sessionId] = Math.max(merged[sessionId] || 0, count);
41
+ Object.entries(clock2).forEach(([client, count]) => {
42
+ merged[client] = Math.max(merged[client] || 0, count);
43
43
  });
44
44
 
45
45
  return merged;
@@ -62,9 +62,9 @@ export class VectorClockManager {
62
62
  let clock1Greater = false;
63
63
  let clock2Greater = false;
64
64
 
65
- allSessionIds.forEach((sessionId) => {
66
- const val1 = clock1[sessionId] || 0;
67
- const val2 = clock2[sessionId] || 0;
65
+ allSessionIds.forEach((client) => {
66
+ const val1 = clock1[client] || 0;
67
+ const val2 = clock2[client] || 0;
68
68
 
69
69
  if (val1 > val2) {
70
70
  clock1Greater = true;
@@ -4,7 +4,7 @@
4
4
  * - VectorClock: Used by client for causal ordering
5
5
  * - DType, ConflictResolver: Server-side conflict resolution for concurrent updates
6
6
  *
7
- * Schema-less design: dtype and operation are derived from otype (e.g., 'vector3.add').
7
+ * Schema-less design: dtype and operation are derived from ot (e.g., 'vector3.add').
8
8
  */
9
9
 
10
10
  // Vector clock (used by client and server)