Sync / Editing server data
A sixty-second path: one column
foldkit-sync/entity keeps the edits one per cell; foldkit-sync/remote
shows them through Remote; foldkit-sync/journal keeps the table. In the
domain, shared by client and server:
ts
import { EditableEntity } from 'foldkit-sync/entity'
export const ProductEdits = EditableEntity.make(Product, { members: ['cents'] })In the application, the durable Message, its fold, and the edits shown through Remote:
ts
const Base = Bundle.compose({
remote: Remote.Model,
edits: Schema.Array(ProductEdits.Edit),
}).pipe(Bundle.withMessages({ ...Remote.messages, EditedProducts: ProductEdits.edited }))
// in update
EditedProducts: ({ changes, at, by }) => ({
model: modifyFields(model, {
edits: edits =>
ProductEdits.merge(edits, changes, Option.fromUndefinedOr(at), Option.fromUndefinedOr(by)),
}),
}),
// every read of a row draws its edits, as Remote overlays
const Shown = RemoteEdits.make(Data, ProductEdits)
// in Sync.mount's options: after every live update, never in replay
afterUpdate: model => ({ model: Shown.reconcile(model, model.edits, replica.replicaId).model }),In the Sync contract, the stamp the journal writes at commit:
ts
stamp: {
EditedProducts: ({ changes }, commit) =>
Message.EditedProducts({ changes, ...ProductEdits.stamped(commit) }),
},On the server, the table:
ts
const { settle, absorb } = editsJournal({
documentId: RegistrySync.documentId,
journal,
editsOf: operation => /* EditedProducts' changes and at, or none */,
// One cell, with its sequence, never moving a row back: foldkit-remote-drizzle's
// applyEdits(Db.Product), over the binding Remote reads the table through.
apply: (change, at) => applyProduct(change, at).pipe(Effect.provide(database)),
// The highest revision any row holds: settling refuses a table past the journal.
tableRevision: Effect.try(() => sqlite.highestRevision()),
holdsThrough: (snapshot, through) => ProductEdits.holdsThrough(snapshot.edits, through),
absorbed: (through, cursor) => /* the server's AbsorbedEdits operation */,
server,
})
// journalExchange({ ..., settle }) applies on every exchange; a clock calls absorb.editsOf and absorbed are the application's Messages, spelled out in
examples/registry/src/journal.ts;
they are elided above. The snippets compile in
examples/registry/test/guide.test-d.ts.