toMap method

  1. @override
Map<String, dynamic> toMap()
override

Converts the CRDT state to a map for serialization.

Implementation

@override
Map<String, dynamic> toMap() {
  // Canonical node order (counter, then replicaId): two CONVERGED replicas
  // must serialize byte-identically. Iterating map-insertion order made
  // each replica serialize its own nodes first, so order-sensitive deep
  // equality in conflict detection re-flagged converged documents as
  // conflicting on every sync cycle, ping-ponging resolution pushes.
  final ordered = _nodes.values.toList()
    ..sort((a, b) {
      final byCounter = a.counter.compareTo(b.counter);
      if (byCounter != 0) return byCounter;
      return a.replicaId.compareTo(b.replicaId);
    });
  // `replicaId` is deliberately NOT serialized: it is per-DEVICE identity,
  // not document state. Serializing it (the old format) made converged
  // replicas serialize differently AND handed deserializing devices the
  // CREATOR's identity as a default — two devices then minted colliding
  // node ids and silently destroyed each other's concurrent edits.
  return {
    'counter': _counter,
    'nodes': [
      for (final n in ordered)
        {
          'r': n.replicaId,
          'c': n.counter,
          'o': n.origin,
          'v': n.value,
          'd': n.deleted,
        },
    ],
  };
}