synk 0.1.0-dev.2
synk: ^0.1.0-dev.2 copied to clipboard
A Shared Editing Library based on CRDTs.
Synk #
Synk is an offline-first conflict-free shared editing library for Dart. Multiple peers can edit the same data at the same time and always end up with exactly the same result, without any server deciding who is right.
Installation #
Add the dependency to your pubspec.yaml:
dart pub add synk
dependencies:
synk: <current-synk-version>
Core Concepts #
Synk is built around three ideas:
SynkDocrepresents your local replica of the shared document. Each peer has their own.SynkMap,SynkBool,SynkDouble,SynkInt,SynkStringare the collaborative data structures you attach to a doc.SynkProtocolis the sync layer that computes minimal binary deltas between peers.
Creating a document #
import 'package:synk/synk.dart';
final doc = SynkDoc();
Each doc gets a random clientId automatically. You can also supply a fixed one:
final doc = SynkDoc(clientId: 42);
Shared Types #
All shared types are attached to a SynkDoc with a unique name key. Two peers using the same name on docs sharing the same history will always converge to the same value.
SynkMap #
A collaborative map (key-value store) that resolves concurrent writes using Last-Writer-Wins (LWW).
final map = SynkMap(doc);
map.set('theme', 'dark');
map.get('theme'); // 'dark'
map.delete('theme');
map.containsKey('theme'); // false
map.toMap(); // {}
Full example -
example/synk_example.dart
SynkInt #
A PN-Counter (Positive-Negative Counter). Concurrent increments from any peer are always fully preserved — there are no conflicts.
final counter = SynkInt(doc, 'score');
counter.increment(); // +1
counter.increment(10); // +10
counter.decrement(3); // -3
counter.value; // 8
Full example -
example/synk_int_example.dart
SynkString #
A single-value string register. Concurrent writes are resolved deterministically via LWW — the write with the higher logical clock wins. If clocks tie, the higher clientId wins.
final title = SynkString(doc, 'title');
title.set('Hello, World!');
title.value; // 'Hello, World!'
Full example -
example/synk_string_example.dart
SynkBool #
Same LWW semantics as SynkString, with an extra toggle() helper.
final flag = SynkBool(doc, 'isPublished');
flag.set(true);
flag.toggle();
flag.value; // false
Full example -
example/synk_bool_example.dart
SynkDouble #
Same LWW semantics as SynkString. JSON-safe: integer payloads from the wire are cast to double transparently.
final price = SynkDouble(doc, 'price');
price.set(9.99);
price.value; // 9.99
Full example -
example/synk_double_example.dart
Syncing Between Peers #
Synk is network-agnostic. You exchange plain Uint8List binary buffers — send them over WebSockets, REST, Bluetooth, or anything else.
The handshake is always the same three steps:
// 1. Peer B encodes what it already has
final bState = SynkProtocol.encodeStateVector(docB);
// 2. Peer A computes only what B is missing
final update = SynkProtocol.encodeStateAsUpdate(docA, bState);
// 3. Peer B applies the update
SynkProtocol.applyUpdate(docB, update);
To do a full sync for a peer joining cold, omit the state vector:
final fullUpdate = SynkProtocol.encodeStateAsUpdate(doc);
SynkProtocol.applyUpdate(newPeerDoc, fullUpdate);
applyUpdate is idempotent — applying the same update twice is always safe.
Full multi-peer example with deletions and a late-joining peer -
example/synk_example.dart
License #
Released under the MIT License. Contributions, bug reports, and PRs are welcome.