rect.sh

State & syncing

The store, optimistic writes, and merge-patch sync.

connect() returns a store over your view model. Reads are synchronous; writes apply optimistically and sync to the host.

import { connect } from '@rectsh/rect';

const rect = await connect();

Reading

rect.get(); // the whole view model
rect.get('items.0.done'); // a dot path — numeric segments index arrays
rect.subscribe((state) => render(state)); // fires on connect + every change
rect.revision; // optimistic-concurrency token (a number)

subscribe returns an unsubscribe function. In React you never call these directly — the hooks do (see React).

Writing

Three write ops, all of which apply locally at once (optimistic), then sync:

rect.set('title', 'Hi'); // set one path
rect.update((draft) => draft.items.push(x)); // mutate a draft
rect.patch({ completion: { approved: true } }); // shallow merge

How syncing works

  1. A write mutates a local copy immediately, so the UI updates with no round trip.
  2. The store diffs the change into an RFC 7386 JSON Merge Patch and sends it to the host.
  3. The host applies the patch to the authoritative store, bumps the revision, and returns the authoritative snapshot.
  4. The store rebases any still-unsynced local edits onto that snapshot.

The same channel delivers changes made by someone else — most importantly an agent calling rect_patch or rect_dispatch. Those arrive as authoritative snapshots and your subscribers fire, so the view stays live without any polling.

Merge-patch semantics

Because sync is a JSON Merge Patch: objects deep-merge, null deletes a key, and arrays replace wholesale (there's no per-index array merge over the wire). To change one array element, write the whole array — or model the collection as an object keyed by id if you want granular updates.

On this page