clone

a deno framework for local-first web apps that open instantly, work offline and sync when users choose

fix/175-template-quality-pass
lofi / docs / examples / collaborative-sets.md
collaborative-sets.md 1.8K rendered
1# Collaborative sets23A g-set column is the right shape when several people add elements to the same collection and4nobody's contribution may be lost: concurrent writers union their elements, and every replica —5including a fresh boot — converges on the same set. This is verified with two synced clients in6`package/schema/merge_sync_test.ts`.78## Declare the set910```ts11// src/schema.ts12import { type ArrayColumn, s } from "@nzip/lofi/schema";1314export const app = s.defineApp({15 tagged: s.table({16 name: s.string(),17 tags: s.array(s.string()).merge("g-set") as unknown as ArrayColumn<"TEXT">,18 }),19});20```2122Two constraints of the pinned alpha shape this declaration:2324- **The cast is required.** `.merge()` returns the untyped builder (a pinned upstream type bug); the25 cast restores the array column type. The runtime object is unchanged.26- **Keep the g-set table in its own single-table app.** A g-set column destabilizes writes to27 sibling tables in the same app — the conformance canary pins the hang. When the model also needs28 ordinary tables, they belong in a separate app until upstream stabilizes this.2930## Write whole sets, converge on the union3132Writers always submit their full local set; the merge takes care of the rest:3334```ts35const doc = await db.insert(app.tagged, { name: "doc", tags: ["a"] }).wait({ tier: "global" });3637// Two clients, offline at the same time:38alice.update(app.tagged, doc.id, { tags: ["a", "b"] });39bob.update(app.tagged, doc.id, { tags: ["a", "c"] });4041// After both reconnect, every replica reads ["a", "b", "c"].42```4344The set is grow-only: the verified behavior is that concurrent writes union. Removing an element is45not part of the verified surface — if entries must be retractable, model the collection as its own46table with one row per element and delete rows under an ordinary policy instead.