;; content-on-sx — op log + versioning over the persist event stream. ;; ;; The op log is the source of truth. Editing a document = appending the edit op ;; as a persist event to the document's stream. Any version of the document is a ;; replay of its op stream up to a sequence number; the materialised doc is a ;; cache, never primary state. ;; ;; Requires (loaded by the harness): block.sx, doc.sx, and persist ;; (event/backend/log/kv/api). The persist backend `b` is opened by the caller ;; via (persist/open) and injected — content knows nothing about which backend. (define content/-stream (fn (doc-id) (str "content:" doc-id))) ;; ── commit: append an edit op as an event. `at` is a caller-supplied logical ;; timestamp (Date.now is unavailable in-kernel). Returns the stored event. ── (define content/commit! (fn (b doc-id op at) (persist/append b (content/-stream doc-id) (get op :op) at op))) (define content/commit-all! (fn (b doc-id ops at) (if (= (len ops) 0) nil (begin (content/commit! b doc-id (first ops) at) (content/commit-all! b doc-id (rest ops) at))))) ;; ── read the raw log / op stream ── (define content/log (fn (b doc-id) (persist/read b (content/-stream doc-id)))) (define content/ops (fn (b doc-id) (map (fn (ev) (persist/event-data ev)) (content/log b doc-id)))) ;; logical version count (highest seq assigned, survives compaction) (define content/version-count (fn (b doc-id) (persist/last-seq b (content/-stream doc-id)))) ;; ── replay ── ;; head — materialise the latest document by folding all ops. (define content/head (fn (b doc-id) (doc-apply-all (doc-empty doc-id) (content/ops b doc-id)))) ;; at — materialise the document as of sequence `seq` (a version). (define content/at (fn (b doc-id seq) (let ((evs (filter (fn (ev) (<= (persist/event-seq ev) seq)) (content/log b doc-id)))) (doc-apply-all (doc-empty doc-id) (map (fn (ev) (persist/event-data ev)) evs))))) ;; ── history: per-version metadata, oldest-first ── (define content/history (fn (b doc-id) (map (fn (ev) {:type (persist/event-type ev) :at (persist/event-at ev) :seq (persist/event-seq ev)}) (content/log b doc-id)))) ;; ── diff between two materialised document versions ── ;; Returns {:added (ids) :removed (ids) :changed (ids)} where changed = ids ;; present in both whose block content differs. (define content/-missing? (fn (doc id) (= (ct-index-of (doc-blocks doc) id) -1))) (define content/-changed (fn (old new) (filter (fn (id) (let ((bo (doc-find old id)) (bn (doc-find new id))) (cond ((= bo nil) false) ((= bn nil) false) ((= bo bn) false) (else true)))) (doc-ids old)))) (define content/diff (fn (old new) {:changed (content/-changed old new) :removed (filter (fn (id) (content/-missing? new id)) (doc-ids old)) :added (filter (fn (id) (content/-missing? old id)) (doc-ids new))})) ;; convenience: diff two persisted versions by seq. (define content/diff-versions (fn (b doc-id seq-a seq-b) (content/diff (content/at b doc-id seq-a) (content/at b doc-id seq-b))))