@vuer-ai/vuer-rtc 0.0.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/client/EditBuffer.d.ts +43 -0
- package/dist/client/EditBuffer.d.ts.map +1 -0
- package/dist/client/EditBuffer.js +96 -0
- package/dist/client/EditBuffer.js.map +1 -0
- package/dist/client/actions.d.ts +66 -0
- package/dist/client/actions.d.ts.map +1 -0
- package/dist/client/actions.js +345 -0
- package/dist/client/actions.js.map +1 -0
- package/dist/client/createGraph.d.ts +30 -0
- package/dist/client/createGraph.d.ts.map +1 -0
- package/dist/client/createGraph.js +91 -0
- package/dist/client/createGraph.js.map +1 -0
- package/dist/client/hooks.d.ts +81 -0
- package/dist/client/hooks.d.ts.map +1 -0
- package/dist/client/hooks.js +161 -0
- package/dist/client/hooks.js.map +1 -0
- package/dist/client/index.d.ts +8 -0
- package/dist/client/index.d.ts.map +1 -0
- package/dist/client/index.js +10 -0
- package/dist/client/index.js.map +1 -0
- package/dist/client/types.d.ts +74 -0
- package/dist/client/types.d.ts.map +1 -0
- package/dist/client/types.js +11 -0
- package/dist/client/types.js.map +1 -0
- package/dist/hooks.d.ts +8 -0
- package/dist/hooks.d.ts.map +1 -0
- package/dist/hooks.js +7 -0
- package/dist/hooks.js.map +1 -0
- package/dist/index.d.ts +9 -0
- package/dist/index.d.ts.map +1 -0
- package/dist/index.js +12 -0
- package/dist/index.js.map +1 -0
- package/dist/operations/OperationTypes.d.ts +239 -0
- package/dist/operations/OperationTypes.d.ts.map +1 -0
- package/dist/operations/OperationTypes.js +10 -0
- package/dist/operations/OperationTypes.js.map +1 -0
- package/dist/operations/OperationValidator.d.ts +32 -0
- package/dist/operations/OperationValidator.d.ts.map +1 -0
- package/dist/operations/OperationValidator.js +208 -0
- package/dist/operations/OperationValidator.js.map +1 -0
- package/dist/operations/apply/array.d.ts +22 -0
- package/dist/operations/apply/array.d.ts.map +1 -0
- package/dist/operations/apply/array.js +64 -0
- package/dist/operations/apply/array.js.map +1 -0
- package/dist/operations/apply/boolean.d.ts +18 -0
- package/dist/operations/apply/boolean.d.ts.map +1 -0
- package/dist/operations/apply/boolean.js +34 -0
- package/dist/operations/apply/boolean.js.map +1 -0
- package/dist/operations/apply/color.d.ts +14 -0
- package/dist/operations/apply/color.d.ts.map +1 -0
- package/dist/operations/apply/color.js +46 -0
- package/dist/operations/apply/color.js.map +1 -0
- package/dist/operations/apply/index.d.ts +18 -0
- package/dist/operations/apply/index.d.ts.map +1 -0
- package/dist/operations/apply/index.js +26 -0
- package/dist/operations/apply/index.js.map +1 -0
- package/dist/operations/apply/node.d.ts +24 -0
- package/dist/operations/apply/node.d.ts.map +1 -0
- package/dist/operations/apply/node.js +77 -0
- package/dist/operations/apply/node.js.map +1 -0
- package/dist/operations/apply/number.d.ts +26 -0
- package/dist/operations/apply/number.d.ts.map +1 -0
- package/dist/operations/apply/number.js +54 -0
- package/dist/operations/apply/number.js.map +1 -0
- package/dist/operations/apply/object.d.ts +14 -0
- package/dist/operations/apply/object.d.ts.map +1 -0
- package/dist/operations/apply/object.js +47 -0
- package/dist/operations/apply/object.js.map +1 -0
- package/dist/operations/apply/quaternion.d.ts +15 -0
- package/dist/operations/apply/quaternion.d.ts.map +1 -0
- package/dist/operations/apply/quaternion.js +33 -0
- package/dist/operations/apply/quaternion.js.map +1 -0
- package/dist/operations/apply/string.d.ts +14 -0
- package/dist/operations/apply/string.d.ts.map +1 -0
- package/dist/operations/apply/string.js +26 -0
- package/dist/operations/apply/string.js.map +1 -0
- package/dist/operations/apply/types.d.ts +34 -0
- package/dist/operations/apply/types.d.ts.map +1 -0
- package/dist/operations/apply/types.js +32 -0
- package/dist/operations/apply/types.js.map +1 -0
- package/dist/operations/apply/vector3.d.ts +18 -0
- package/dist/operations/apply/vector3.d.ts.map +1 -0
- package/dist/operations/apply/vector3.js +44 -0
- package/dist/operations/apply/vector3.js.map +1 -0
- package/dist/operations/dispatcher.d.ts +35 -0
- package/dist/operations/dispatcher.d.ts.map +1 -0
- package/dist/operations/dispatcher.js +107 -0
- package/dist/operations/dispatcher.js.map +1 -0
- package/dist/operations/index.d.ts +10 -0
- package/dist/operations/index.d.ts.map +1 -0
- package/dist/operations/index.js +17 -0
- package/dist/operations/index.js.map +1 -0
- package/dist/state/ConflictResolver.d.ts +36 -0
- package/dist/state/ConflictResolver.d.ts.map +1 -0
- package/dist/state/ConflictResolver.js +167 -0
- package/dist/state/ConflictResolver.js.map +1 -0
- package/dist/state/DType.d.ts +160 -0
- package/dist/state/DType.d.ts.map +1 -0
- package/dist/state/DType.js +282 -0
- package/dist/state/DType.js.map +1 -0
- package/dist/state/Schema.d.ts +32 -0
- package/dist/state/Schema.d.ts.map +1 -0
- package/dist/state/Schema.js +175 -0
- package/dist/state/Schema.js.map +1 -0
- package/dist/state/VectorClock.d.ts +42 -0
- package/dist/state/VectorClock.d.ts.map +1 -0
- package/dist/state/VectorClock.js +84 -0
- package/dist/state/VectorClock.js.map +1 -0
- package/dist/state/index.d.ts +11 -0
- package/dist/state/index.d.ts.map +1 -0
- package/dist/state/index.js +13 -0
- package/dist/state/index.js.map +1 -0
- package/docs/OPERATION_HINTS.md +222 -0
- package/docs/SCENE_GRAPH.md +373 -0
- package/docs/TYPE_BEHAVIORS.md +348 -0
- package/examples/01-basic-usage.ts +139 -0
- package/examples/02-concurrent-edits.ts +208 -0
- package/examples/03-scene-building.ts +258 -0
- package/examples/04-conflict-resolution.ts +339 -0
- package/examples/README.md +86 -0
- package/jest.config.js +19 -0
- package/package.json +57 -0
- package/src/client/EditBuffer.ts +105 -0
- package/src/client/actions.ts +397 -0
- package/src/client/createGraph.ts +132 -0
- package/src/client/hooks.tsx +249 -0
- package/src/client/index.ts +35 -0
- package/src/client/types.ts +94 -0
- package/src/hooks.ts +20 -0
- package/src/index.ts +14 -0
- package/src/operations/OperationTypes.ts +340 -0
- package/src/operations/OperationValidator.ts +260 -0
- package/src/operations/apply/array.ts +84 -0
- package/src/operations/apply/boolean.ts +48 -0
- package/src/operations/apply/color.ts +65 -0
- package/src/operations/apply/index.ts +37 -0
- package/src/operations/apply/node.ts +98 -0
- package/src/operations/apply/number.ts +76 -0
- package/src/operations/apply/object.ts +63 -0
- package/src/operations/apply/quaternion.ts +47 -0
- package/src/operations/apply/string.ts +36 -0
- package/src/operations/apply/types.ts +66 -0
- package/src/operations/apply/vector3.ts +60 -0
- package/src/operations/dispatcher.ts +127 -0
- package/src/operations/index.ts +80 -0
- package/src/state/ConflictResolver.ts +205 -0
- package/src/state/DType.ts +333 -0
- package/src/state/Schema.ts +236 -0
- package/src/state/VectorClock.ts +98 -0
- package/src/state/index.ts +14 -0
- package/tests/client/actions.test.ts +371 -0
- package/tests/client/edit-buffer.test.ts +117 -0
- package/tests/fixtures/array-ops.jsonl +6 -0
- package/tests/fixtures/boolean-ops.jsonl +6 -0
- package/tests/fixtures/color-ops.jsonl +4 -0
- package/tests/fixtures/edit-buffer.jsonl +3 -0
- package/tests/fixtures/node-ops.jsonl +6 -0
- package/tests/fixtures/number-ops.jsonl +7 -0
- package/tests/fixtures/object-ops.jsonl +4 -0
- package/tests/fixtures/operations.jsonl +7 -0
- package/tests/fixtures/string-ops.jsonl +4 -0
- package/tests/fixtures/undo-redo.jsonl +3 -0
- package/tests/fixtures/vector-ops.jsonl +9 -0
- package/tests/operations/collections.test.ts +193 -0
- package/tests/operations/nodes.test.ts +228 -0
- package/tests/operations/primitives.test.ts +222 -0
- package/tests/operations/vectors.test.ts +150 -0
- package/tsconfig.json +21 -0
- package/tsconfig.test.json +9 -0
|
@@ -0,0 +1,127 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Operation Dispatcher
|
|
3
|
+
*
|
|
4
|
+
* Applies CRDTMessage operations to a SceneGraph using immer.
|
|
5
|
+
* Each operation is dispatched to its corresponding "apply" function.
|
|
6
|
+
*/
|
|
7
|
+
|
|
8
|
+
import { produce, enableMapSet } from 'immer';
|
|
9
|
+
import type { SceneGraph, CRDTMessage, Operation } from './OperationTypes.js';
|
|
10
|
+
import type { OpMeta } from './apply/types.js';
|
|
11
|
+
import * as registry from './apply/index.js';
|
|
12
|
+
|
|
13
|
+
// Enable immer support for Map and Set
|
|
14
|
+
enableMapSet();
|
|
15
|
+
|
|
16
|
+
/**
|
|
17
|
+
* Handler map: otype -> apply function
|
|
18
|
+
*/
|
|
19
|
+
const handlers: Record<string, (draft: SceneGraph, op: Operation, meta: OpMeta) => void> = {
|
|
20
|
+
// Number operations
|
|
21
|
+
'number.set': registry.NumberSet as (draft: SceneGraph, op: Operation, meta: OpMeta) => void,
|
|
22
|
+
'number.add': registry.NumberAdd as (draft: SceneGraph, op: Operation, meta: OpMeta) => void,
|
|
23
|
+
'number.multiply': registry.NumberMultiply as (draft: SceneGraph, op: Operation, meta: OpMeta) => void,
|
|
24
|
+
'number.min': registry.NumberMin as (draft: SceneGraph, op: Operation, meta: OpMeta) => void,
|
|
25
|
+
'number.max': registry.NumberMax as (draft: SceneGraph, op: Operation, meta: OpMeta) => void,
|
|
26
|
+
|
|
27
|
+
// String operations
|
|
28
|
+
'string.set': registry.StringSet as (draft: SceneGraph, op: Operation, meta: OpMeta) => void,
|
|
29
|
+
'string.concat': registry.StringConcat as (draft: SceneGraph, op: Operation, meta: OpMeta) => void,
|
|
30
|
+
|
|
31
|
+
// Boolean operations
|
|
32
|
+
'boolean.set': registry.BooleanSet as (draft: SceneGraph, op: Operation, meta: OpMeta) => void,
|
|
33
|
+
'boolean.or': registry.BooleanOr as (draft: SceneGraph, op: Operation, meta: OpMeta) => void,
|
|
34
|
+
'boolean.and': registry.BooleanAnd as (draft: SceneGraph, op: Operation, meta: OpMeta) => void,
|
|
35
|
+
|
|
36
|
+
// Vector3 operations
|
|
37
|
+
'vector3.set': registry.Vector3Set as (draft: SceneGraph, op: Operation, meta: OpMeta) => void,
|
|
38
|
+
'vector3.add': registry.Vector3Add as (draft: SceneGraph, op: Operation, meta: OpMeta) => void,
|
|
39
|
+
'vector3.multiply': registry.Vector3Multiply as (draft: SceneGraph, op: Operation, meta: OpMeta) => void,
|
|
40
|
+
|
|
41
|
+
// Quaternion operations
|
|
42
|
+
'quaternion.set': registry.QuaternionSet as (draft: SceneGraph, op: Operation, meta: OpMeta) => void,
|
|
43
|
+
'quaternion.multiply': registry.QuaternionMultiply as (draft: SceneGraph, op: Operation, meta: OpMeta) => void,
|
|
44
|
+
|
|
45
|
+
// Color operations
|
|
46
|
+
'color.set': registry.ColorSet as (draft: SceneGraph, op: Operation, meta: OpMeta) => void,
|
|
47
|
+
'color.blend': registry.ColorBlend as (draft: SceneGraph, op: Operation, meta: OpMeta) => void,
|
|
48
|
+
|
|
49
|
+
// Array operations
|
|
50
|
+
'array.set': registry.ArraySet as (draft: SceneGraph, op: Operation, meta: OpMeta) => void,
|
|
51
|
+
'array.push': registry.ArrayPush as (draft: SceneGraph, op: Operation, meta: OpMeta) => void,
|
|
52
|
+
'array.remove': registry.ArrayRemove as (draft: SceneGraph, op: Operation, meta: OpMeta) => void,
|
|
53
|
+
'array.union': registry.ArrayUnion as (draft: SceneGraph, op: Operation, meta: OpMeta) => void,
|
|
54
|
+
|
|
55
|
+
// Object operations
|
|
56
|
+
'object.set': registry.ObjectSet as (draft: SceneGraph, op: Operation, meta: OpMeta) => void,
|
|
57
|
+
'object.merge': registry.ObjectMerge as (draft: SceneGraph, op: Operation, meta: OpMeta) => void,
|
|
58
|
+
|
|
59
|
+
// Node operations
|
|
60
|
+
'node.insert': registry.NodeInsert as (draft: SceneGraph, op: Operation, meta: OpMeta) => void,
|
|
61
|
+
'node.remove': registry.NodeRemove as (draft: SceneGraph, op: Operation, meta: OpMeta) => void,
|
|
62
|
+
};
|
|
63
|
+
|
|
64
|
+
/**
|
|
65
|
+
* Apply a single operation to a SceneGraph draft
|
|
66
|
+
*/
|
|
67
|
+
export function applyOperation(
|
|
68
|
+
draft: SceneGraph,
|
|
69
|
+
op: Operation,
|
|
70
|
+
meta: OpMeta
|
|
71
|
+
): void {
|
|
72
|
+
const handler = handlers[op.otype];
|
|
73
|
+
if (handler) {
|
|
74
|
+
handler(draft, op, meta);
|
|
75
|
+
} else {
|
|
76
|
+
console.warn(`Unknown otype: ${op.otype}`);
|
|
77
|
+
}
|
|
78
|
+
}
|
|
79
|
+
|
|
80
|
+
/**
|
|
81
|
+
* Apply a CRDTMessage to a SceneGraph (immutable)
|
|
82
|
+
*
|
|
83
|
+
* Uses immer to produce a new state without mutating the original.
|
|
84
|
+
*
|
|
85
|
+
* @param graph - Current scene graph state
|
|
86
|
+
* @param msg - CRDT message containing operations
|
|
87
|
+
* @returns New scene graph state with operations applied
|
|
88
|
+
*/
|
|
89
|
+
export function applyMessage(graph: SceneGraph, msg: CRDTMessage): SceneGraph {
|
|
90
|
+
return produce(graph, (draft) => {
|
|
91
|
+
const meta: OpMeta = {
|
|
92
|
+
sessionId: msg.sessionId,
|
|
93
|
+
clock: msg.clock,
|
|
94
|
+
lamportTime: msg.lamportTime,
|
|
95
|
+
timestamp: msg.timestamp,
|
|
96
|
+
};
|
|
97
|
+
|
|
98
|
+
for (const op of msg.ops) {
|
|
99
|
+
applyOperation(draft, op, meta);
|
|
100
|
+
}
|
|
101
|
+
});
|
|
102
|
+
}
|
|
103
|
+
|
|
104
|
+
/**
|
|
105
|
+
* Apply multiple CRDTMessages to a SceneGraph (immutable)
|
|
106
|
+
*
|
|
107
|
+
* @param graph - Current scene graph state
|
|
108
|
+
* @param messages - Array of CRDT messages to apply
|
|
109
|
+
* @returns New scene graph state with all operations applied
|
|
110
|
+
*/
|
|
111
|
+
export function applyMessages(graph: SceneGraph, messages: CRDTMessage[]): SceneGraph {
|
|
112
|
+
let current = graph;
|
|
113
|
+
for (const msg of messages) {
|
|
114
|
+
current = applyMessage(current, msg);
|
|
115
|
+
}
|
|
116
|
+
return current;
|
|
117
|
+
}
|
|
118
|
+
|
|
119
|
+
/**
|
|
120
|
+
* Create an empty scene graph
|
|
121
|
+
*/
|
|
122
|
+
export function createEmptyGraph(): SceneGraph {
|
|
123
|
+
return {
|
|
124
|
+
nodes: {},
|
|
125
|
+
rootKey: '',
|
|
126
|
+
};
|
|
127
|
+
}
|
|
@@ -0,0 +1,80 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Operations module exports
|
|
3
|
+
*/
|
|
4
|
+
|
|
5
|
+
// ============================================
|
|
6
|
+
// Operation Types
|
|
7
|
+
// ============================================
|
|
8
|
+
|
|
9
|
+
export type {
|
|
10
|
+
CRDTMessage,
|
|
11
|
+
BaseOp,
|
|
12
|
+
// Number ops
|
|
13
|
+
NumberSetOp,
|
|
14
|
+
NumberAddOp,
|
|
15
|
+
NumberMultiplyOp,
|
|
16
|
+
NumberMinOp,
|
|
17
|
+
NumberMaxOp,
|
|
18
|
+
// String ops
|
|
19
|
+
StringSetOp,
|
|
20
|
+
StringConcatOp,
|
|
21
|
+
// Boolean ops
|
|
22
|
+
BooleanSetOp,
|
|
23
|
+
BooleanOrOp,
|
|
24
|
+
BooleanAndOp,
|
|
25
|
+
// Vector3 ops
|
|
26
|
+
Vector3SetOp,
|
|
27
|
+
Vector3AddOp,
|
|
28
|
+
Vector3MultiplyOp,
|
|
29
|
+
// Quaternion ops
|
|
30
|
+
QuaternionSetOp,
|
|
31
|
+
QuaternionMultiplyOp,
|
|
32
|
+
// Color ops
|
|
33
|
+
ColorSetOp,
|
|
34
|
+
ColorBlendOp,
|
|
35
|
+
// Array ops
|
|
36
|
+
ArraySetOp,
|
|
37
|
+
ArrayPushOp,
|
|
38
|
+
ArrayUnionOp,
|
|
39
|
+
ArrayRemoveOp,
|
|
40
|
+
// Object ops
|
|
41
|
+
ObjectSetOp,
|
|
42
|
+
ObjectMergeOp,
|
|
43
|
+
// Scene graph ops
|
|
44
|
+
NodeInsertOp,
|
|
45
|
+
NodeRemoveOp,
|
|
46
|
+
// Meta ops (undo/redo)
|
|
47
|
+
MetaUndoOp,
|
|
48
|
+
MetaRedoOp,
|
|
49
|
+
// Union
|
|
50
|
+
Operation,
|
|
51
|
+
// Scene graph types
|
|
52
|
+
SceneNode,
|
|
53
|
+
SceneGraph,
|
|
54
|
+
} from './OperationTypes.js';
|
|
55
|
+
|
|
56
|
+
// ============================================
|
|
57
|
+
// Validation
|
|
58
|
+
// ============================================
|
|
59
|
+
|
|
60
|
+
export { OperationValidator, type ValidationResult } from './OperationValidator.js';
|
|
61
|
+
|
|
62
|
+
// ============================================
|
|
63
|
+
// Apply Registry (individual apply functions)
|
|
64
|
+
// ============================================
|
|
65
|
+
|
|
66
|
+
import * as registry from './apply/index.js';
|
|
67
|
+
export { registry };
|
|
68
|
+
|
|
69
|
+
export type { OpMeta, ApplyFn } from './apply/types.js';
|
|
70
|
+
|
|
71
|
+
// ============================================
|
|
72
|
+
// Dispatcher (high-level API)
|
|
73
|
+
// ============================================
|
|
74
|
+
|
|
75
|
+
export {
|
|
76
|
+
applyMessage,
|
|
77
|
+
applyMessages,
|
|
78
|
+
applyOperation,
|
|
79
|
+
createEmptyGraph,
|
|
80
|
+
} from './dispatcher.js';
|
|
@@ -0,0 +1,205 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* ConflictResolver - Schema-based property conflict resolution
|
|
3
|
+
*
|
|
4
|
+
* Merges concurrent property updates based on:
|
|
5
|
+
* - Property schema (dtype + operation)
|
|
6
|
+
* - DType merge functions
|
|
7
|
+
*/
|
|
8
|
+
|
|
9
|
+
import { DType, type ValueWithMeta } from './DType.js';
|
|
10
|
+
import { getPropertySchema, isImmutable } from './Schema.js';
|
|
11
|
+
|
|
12
|
+
export class ConflictResolver {
|
|
13
|
+
/**
|
|
14
|
+
* Merge a single property from multiple concurrent updates
|
|
15
|
+
*
|
|
16
|
+
* @param propertyName - Name of the property being merged
|
|
17
|
+
* @param values - Array of values with metadata
|
|
18
|
+
* @param operationHint - Optional operation override from UPDATE operation
|
|
19
|
+
* @returns Merged value
|
|
20
|
+
*/
|
|
21
|
+
mergeProperty<T = any>(
|
|
22
|
+
propertyName: string,
|
|
23
|
+
values: ValueWithMeta<T>[],
|
|
24
|
+
operationHint?: string
|
|
25
|
+
): T {
|
|
26
|
+
if (values.length === 0) {
|
|
27
|
+
throw new Error(`Cannot merge empty values for property: ${propertyName}`);
|
|
28
|
+
}
|
|
29
|
+
|
|
30
|
+
if (values.length === 1) {
|
|
31
|
+
return values[0].value;
|
|
32
|
+
}
|
|
33
|
+
|
|
34
|
+
// Get schema for this property
|
|
35
|
+
const schema = getPropertySchema(propertyName);
|
|
36
|
+
|
|
37
|
+
// Immutable properties: return earliest value
|
|
38
|
+
if (isImmutable(propertyName)) {
|
|
39
|
+
const sorted = [...values].sort((a, b) => a.lamportTime - b.lamportTime);
|
|
40
|
+
return sorted[0].value;
|
|
41
|
+
}
|
|
42
|
+
|
|
43
|
+
// Use operation hint if provided, otherwise use schema
|
|
44
|
+
const operation = operationHint || schema.operation;
|
|
45
|
+
|
|
46
|
+
// Get merge function based on dtype and operation
|
|
47
|
+
const mergeFn = this.getMergeFunction(schema.dtype, operation);
|
|
48
|
+
|
|
49
|
+
if (!mergeFn) {
|
|
50
|
+
console.warn(
|
|
51
|
+
`No merge function for dtype=${schema.dtype} operation=${operation}, defaulting to LWW`
|
|
52
|
+
);
|
|
53
|
+
return DType.string.set(values as any) as any;
|
|
54
|
+
}
|
|
55
|
+
|
|
56
|
+
return mergeFn(values);
|
|
57
|
+
}
|
|
58
|
+
|
|
59
|
+
/**
|
|
60
|
+
* Merge multiple properties from concurrent updates
|
|
61
|
+
*
|
|
62
|
+
* @param updates - Array of property updates with metadata and operation hints
|
|
63
|
+
* @returns Merged properties
|
|
64
|
+
*/
|
|
65
|
+
mergeProperties(
|
|
66
|
+
updates: Array<{
|
|
67
|
+
properties: Record<string, any>;
|
|
68
|
+
operations?: Record<string, string>; // Operation hints per property
|
|
69
|
+
lamportTime: number;
|
|
70
|
+
sessionId: string;
|
|
71
|
+
}>
|
|
72
|
+
): Record<string, any> {
|
|
73
|
+
// Group values by property name
|
|
74
|
+
const valuesByProperty = new Map<string, ValueWithMeta[]>();
|
|
75
|
+
const operationHints = new Map<string, string>();
|
|
76
|
+
|
|
77
|
+
for (const update of updates) {
|
|
78
|
+
for (const [key, value] of Object.entries(update.properties)) {
|
|
79
|
+
if (key === 'operations') continue; // Skip operations field
|
|
80
|
+
|
|
81
|
+
if (!valuesByProperty.has(key)) {
|
|
82
|
+
valuesByProperty.set(key, []);
|
|
83
|
+
}
|
|
84
|
+
|
|
85
|
+
valuesByProperty.get(key)!.push({
|
|
86
|
+
value,
|
|
87
|
+
lamportTime: update.lamportTime,
|
|
88
|
+
sessionId: update.sessionId,
|
|
89
|
+
});
|
|
90
|
+
|
|
91
|
+
// Store operation hint if provided
|
|
92
|
+
if (update.operations?.[key]) {
|
|
93
|
+
operationHints.set(key, update.operations[key]);
|
|
94
|
+
}
|
|
95
|
+
}
|
|
96
|
+
}
|
|
97
|
+
|
|
98
|
+
// Merge each property
|
|
99
|
+
const result: Record<string, any> = {};
|
|
100
|
+
|
|
101
|
+
for (const [propertyName, values] of valuesByProperty.entries()) {
|
|
102
|
+
const operationHint = operationHints.get(propertyName);
|
|
103
|
+
result[propertyName] = this.mergeProperty(propertyName, values, operationHint);
|
|
104
|
+
}
|
|
105
|
+
|
|
106
|
+
return result;
|
|
107
|
+
}
|
|
108
|
+
|
|
109
|
+
/**
|
|
110
|
+
* Get merge function for dtype and operation
|
|
111
|
+
*/
|
|
112
|
+
private getMergeFunction(dtype: string, operation: string): ((values: ValueWithMeta[]) => any) | null {
|
|
113
|
+
switch (dtype) {
|
|
114
|
+
case 'number':
|
|
115
|
+
switch (operation) {
|
|
116
|
+
case 'set':
|
|
117
|
+
return DType.number.set;
|
|
118
|
+
case 'add':
|
|
119
|
+
return DType.number.add;
|
|
120
|
+
case 'multiply':
|
|
121
|
+
return DType.number.multiply;
|
|
122
|
+
case 'min':
|
|
123
|
+
return DType.number.min;
|
|
124
|
+
case 'max':
|
|
125
|
+
return DType.number.max;
|
|
126
|
+
}
|
|
127
|
+
break;
|
|
128
|
+
|
|
129
|
+
case 'string':
|
|
130
|
+
switch (operation) {
|
|
131
|
+
case 'set':
|
|
132
|
+
return DType.string.set;
|
|
133
|
+
case 'concat':
|
|
134
|
+
return DType.string.concat as any;
|
|
135
|
+
}
|
|
136
|
+
break;
|
|
137
|
+
|
|
138
|
+
case 'boolean':
|
|
139
|
+
switch (operation) {
|
|
140
|
+
case 'set':
|
|
141
|
+
return DType.boolean.set;
|
|
142
|
+
case 'or':
|
|
143
|
+
return DType.boolean.or;
|
|
144
|
+
case 'and':
|
|
145
|
+
return DType.boolean.and;
|
|
146
|
+
}
|
|
147
|
+
break;
|
|
148
|
+
|
|
149
|
+
case 'vector3':
|
|
150
|
+
switch (operation) {
|
|
151
|
+
case 'set':
|
|
152
|
+
return DType.vector3.set;
|
|
153
|
+
case 'add':
|
|
154
|
+
return DType.vector3.add;
|
|
155
|
+
case 'multiply':
|
|
156
|
+
return DType.vector3.multiply;
|
|
157
|
+
}
|
|
158
|
+
break;
|
|
159
|
+
|
|
160
|
+
case 'quaternion':
|
|
161
|
+
switch (operation) {
|
|
162
|
+
case 'set':
|
|
163
|
+
return DType.quaternion.set;
|
|
164
|
+
case 'multiply':
|
|
165
|
+
return DType.quaternion.multiply;
|
|
166
|
+
}
|
|
167
|
+
break;
|
|
168
|
+
|
|
169
|
+
case 'color':
|
|
170
|
+
switch (operation) {
|
|
171
|
+
case 'set':
|
|
172
|
+
return DType.color.set;
|
|
173
|
+
case 'blend':
|
|
174
|
+
return DType.color.blend;
|
|
175
|
+
}
|
|
176
|
+
break;
|
|
177
|
+
|
|
178
|
+
case 'array':
|
|
179
|
+
switch (operation) {
|
|
180
|
+
case 'set':
|
|
181
|
+
return DType.array.set;
|
|
182
|
+
case 'union':
|
|
183
|
+
return DType.array.union;
|
|
184
|
+
case 'append':
|
|
185
|
+
return DType.array.append;
|
|
186
|
+
}
|
|
187
|
+
break;
|
|
188
|
+
|
|
189
|
+
case 'object':
|
|
190
|
+
switch (operation) {
|
|
191
|
+
case 'set':
|
|
192
|
+
return DType.object.set;
|
|
193
|
+
case 'lww-per-key':
|
|
194
|
+
return DType.object.lwwPerKey;
|
|
195
|
+
}
|
|
196
|
+
break;
|
|
197
|
+
|
|
198
|
+
case 'any':
|
|
199
|
+
// Default to LWW for 'any' type
|
|
200
|
+
return DType.string.set as any;
|
|
201
|
+
}
|
|
202
|
+
|
|
203
|
+
return null;
|
|
204
|
+
}
|
|
205
|
+
}
|