@asterql/sync
v0.5.0
Published
Multiplexed scope-subscribed sync client: one connection, server event envelopes, cursor resume.
Maintainers
Readme
@asterql/sync
One connection per session for AsterQL state sync: a multiplexed,
scope-subscribed WebSocket client speaking the @asterql/view-protocol
sync wire vocabulary, with per-scope handlers and cursor resume.
Install
npm install @asterql/sync @asterql/view-protocolUsage
import { SyncClient } from "@asterql/sync";
import { EntityStore } from "@asterql/store";
const client = new SyncClient({
url: "wss://app.example.com/api/sync/socket",
// browser: defaults to globalThis.WebSocket; Node/TUI: pass `ws`
});
const store = new EntityStore();
store.cursors.beginScope("run:run_1", 0);
const unsubscribe = client.subscribe("run:run_1", {
onEnvelope: (envelope) => store.ingest(envelope).result.kind,
since: () => store.cursors.scopeLastSeq("run:run_1"),
onSnapshotRequired: async () => {
// fetch a registered view (Protocol A), seed the store, then
// store.cursors.beginScope("run:run_1", seqFromSnapshot)
},
});
// later
unsubscribe();Semantics
- One socket, many scopes, many stores. Each
subscribe(scope, handler)owns that scope's ingestion: an identity scope can drive cache invalidation while each run scope feeds its own store, all over one connection. Subscriptions are reference-counted per scope. - Cursor resume.
sub.sincecomes from the handler'ssince(); omitted means live-from-now (the server starts at its current head). Reconnects resubscribe every scope from its cursor, so missed envelopes replay from the server's ledger. - Gap replay. A handler returning
"gap"fromonEnvelopetriggers a replay request from its cursor; out-of-order events are never applied. - Bootstrap handoff.
scopeError snapshot_required(cursor beyond the server's replay horizon) callsonSnapshotRequired(); after the caller re-seeds, the client resubscribes from the new cursor. - Liveness. Heartbeat ping/pong (two missed pongs recycle the socket) and exponential reconnect backoff.
Server expectations
The host owns the endpoint: authenticate at the upgrade handshake,
authorize per scope, persist envelopes in per-scope ledgers with
contiguous sequence numbers, replay from sub.since, and emit a seed
snapshot as each scope's first ledger entry so replay-from-0 is
deterministic. The reference server lives in Fridayland
(packages/server/src/sync-socket.ts).
