accordsync library
The Accord client for Dart: local-first writes, background sync, conflicts and refusals.
A port of @accordsync/client. Pure Dart: no Flutter needed (see accordsync_flutter for
storage on devices and widgets).
Classes
- AccordClient
-
An Accord device. Writes apply locally at once and are saved to storage; sync pushes them and
pulls everyone else's, in the background or on demand. Port of
@accordsync/client. - AddOp
- Adds an element to a set; the op id is its tag. deps lists the element's tags the writer could see: the add replaces them, while a concurrent remove still loses.
- AssignOp
-
Writes a value. For
lwwthe highest clock wins; forconflict, deps lists the values the writer could see, and the assign supersedes exactly those (ADR-0003). - ConflictInfo
-
A
conflict()field holding more than one value. Equal when the record, field and values are. - ConflictState
- CounterState
- ExitItem
- The record left this device's read scope (ADR-0004).
- FieldState
-
Per-field state. Each strategy's applyOp is commutative and associative over distinct ops:
any delivery order of the same ops gives the same state. The replica applies each op at most
once, which makes the merge idempotent. Port of
strategies.ts. - Hlc
- Hybrid logical clock: physical time + logical counter + node id.
- HttpTransport
- Talks to an Accord server over HTTPS (see the server's docs/protocol.md).
- IncOp
- Adds by (a positive or negative integer) to a counter.
- LocalWriter
-
One device's replica plus the means to write to it: every local write becomes an op, applied
locally first and returned so the caller can queue it for sync. Port of
writer.ts. - LwwState
- MemoryStorage
- In-memory storage: for tests, and for apps that accept losing unsynced writes on restart.
- Op
-
One change to one field of one record. Port of
op.ts. - OpItem
- An op, in the wire format.
- PullItem
- One item of a pull page.
- PullPage
- PullResult
- PushResult
- The server's answer to a push.
- RecordSnapshot
- A record's state with its history folded away (log compaction, ADR-0008).
- RemoveOp
- Removes the add tags in deps (the ones the writer had seen); concurrent adds survive.
- Replica
-
An op log and the state projected from it. Pure: no I/O, no clock. Two replicas holding the
same set of ops always read the same state, whatever order the ops arrived in. Port of
replica.ts. - ResyncRequired
- The server asks the device to drop its data and pull from zero.
- SetState
- SnapshotItem
- A compacted record (ADR-0008).
- StorageAdapter
- Durable storage for a device. commit must be atomic: after a crash, either all of a transaction is visible or none of it.
- StorageSnapshot
- Everything a device has stored.
- StorageTx
- One atomic change. Applied in this order: clear, delete, put, outbox changes, meta.
- StoredMeta
- What a device must remember between launches.
- Transport
- How a client reaches the server. HttpTransport is the real one; tests can fake it.
Enums
- ApplyResult
- Strategy
-
How a field merges concurrent writes. Port of
schema.ts.
Constants
- absent → const Object
-
Marks a field with no value (never written): left out of reads and snapshots, like JavaScript's
undefined. A field written withnullreads asnull. - defaultMaxSkewMs → const int
-
kinds
→ const Map<
Strategy, List< String> > - The op kinds each strategy accepts.
- maxCounter → const int
- The largest counter value; one more rolls the clock into the next millisecond.
- maxSafeInteger → const int
- The largest integer JavaScript represents exactly: clocks and counts stay within it.
- protocolVersion → const int
- Version of the sync protocol spoken between Accord clients and servers.
Functions
-
applyOp(
FieldState state, Op op) → void -
Applies
optostatein place. The op must already be validated against the schema. -
assertNode(
String node) → void -
Throws unless
nodeis a valid node (device) id:[A-Za-z0-9_-]{1,64}. -
canonicalJson(
Object? value) → String - JSON with object keys in a canonical order, so equal values always serialise to the same string.
-
compareElements(
Object a, Object b) → int - Numbers first (ascending), then strings (by UTF-16 code unit), as in the TypeScript core.
-
compareOpIds(
OpId a, OpId b) → int -
Sort order for op ids: UTF-16 code units, exactly like JavaScript's
<on strings. -
conflict(
) → Strategy -
counter(
) → Strategy -
decodeOp(
Object? input) → Op - Parses untrusted input into an op, or throws with the reason. Schema checks happen later.
-
defineSchema(
Schema schema) → Schema - Checks and returns a schema.
-
emptyState(
Strategy strategy) → FieldState -
encodeOp(
Op op) → Map< String, Object?> -
An op as it travels over the network and sits in golden vectors. Port of
wire.ts. -
fieldObservedDeps(
FieldState state, [Object? element]) → List< OpId> -
Op ids a writer must cite in
deps: live conflict values, or the tags of a set element. -
fieldsOf(
Schema schema, String record) → Map< String, Strategy> -
jsKeyOrder(
Iterable< String> keys) → List<String> - The order JavaScript gives the keys of an object built from code-unit-sorted entries.
-
jsNumber(
num n) → String -
A number as
JSON.stringifyprints it. -
lww(
) → Strategy -
lww(),counter(),set(),conflict(): the same declarations as the TypeScript schema. -
parseOpId(
String opId) → ({String device, int seq}) - The device and sequence number of an op id.
-
randomDeviceId(
[Random? random]) → String -
A random device id from a secure source (
Random.secure). Device ids must never collide, and are never derived from the hardware. -
readState(
FieldState state) → Object? -
A field as the app sees it: a JSON value, a sorted list (set), an int (counter), or for a
conflict()field{'value': v}/{'conflicted': [{'value': v, 'opId': id}, …]}. absent when never written. -
recordType(
String record) → String -
The type of a record id (
dossierfordossier:91). -
sameElement(
Object? a, Object? b) → bool -
Set elements compare like JavaScript's
===:1and1.0are the same element. -
set(
) → Strategy -
snapshotState(
FieldState state) → Map< String, Object?> -
A field's live state as JSON; tombstones are dropped (ADR-0008). Same shape as the TypeScript
FieldSnapshot, so snapshots from the server load here unchanged. -
stateFromSnapshot(
Map< String, Object?> snap) → FieldState -
strategyFor(
Schema schema, String record, String field) → Strategy -
The strategy for
record.field, or an error naming what is wrong.
Typedefs
- ConflictValue = ({OpId opId, Object? value})
- One of the values a conflicted field holds, and the op that wrote it.
- OpId = String
-
deviceId:sequence. Unique per op, so applying an op twice is a no-op. - Refusal = ({String field, OpId opId, String reason, String record})
- A local write the server refused. It has already been rolled back on this device.
-
Schema
= Map<
String, Map< String, Strategy> > - Record type → field → strategy.
- SyncStatus = ({int cursor, Object? lastError, int? lastSyncAt, int pending})
- Where sync stands.
Exceptions / Errors
- AccordException
- An op, record or schema that Accord refuses, with the reason.
- ClockSkewException
- A remote clock too far ahead of this one: the op is refused instead of winning every merge.
- HttpError