Something went wrong. Try again.
a proof of concept realtime collaborative todo list
Something went wrong. Try again.
12345678910111213141516171819202122232425262728293031323334353637383940414243444546474849505152535455// Each item record carries a $crdt map: { <field>: { clock, actor } }.// Merge picks per-field winner by highest clock, lex-greater actor as tiebreak.
export function mergeRecords(records) { if (records.length === 0) return null; const fields = new Set(); for (const r of records) for (const k of Object.keys(r.$crdt || {})) fields.add(k);
const out = { $crdt: {} }; for (const field of fields) { let best = null; for (const r of records) { const meta = r.$crdt?.[field]; if (!meta) continue; if ( !best || meta.clock > best.meta.clock || (meta.clock === best.meta.clock && meta.actor > best.meta.actor) ) { best = { value: r[field], meta }; } } if (best) { out[field] = best.value; out.$crdt[field] = best.meta; } } return out;}
// Build the record we'll write into our own repo. Each editor's record stays// fully up-to-date: copy every field from the merged state, then bump $crdt// (clock + actor) for the fields we're explicitly changing.export function crdtEqual(a, b) { const ak = Object.keys(a || {}); const bk = Object.keys(b || {}); if (ak.length !== bk.length) return false; for (const k of ak) { const x = a[k]; const y = b[k]; if (!y || x.clock !== y.clock || x.actor !== y.actor) return false; } return true;}
export function applyUpdates(merged, updates, actor) { const record = merged ? { ...merged, $crdt: { ...(merged.$crdt || {}) } } : { $crdt: {} }; for (const [field, value] of Object.entries(updates)) { record[field] = value; const prevClock = merged?.$crdt?.[field]?.clock ?? 0; record.$crdt[field] = { clock: prevClock + 1, actor }; } return record;}