content: persist-backed op log + versioning + diff (Phase 2 complete, 162/162)
Some checks failed
Test, Build, and Deploy / test-build-deploy (push) Failing after 53s
Some checks failed
Test, Build, and Deploy / test-build-deploy (push) Failing after 53s
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
This commit is contained in:
101
lib/content/store.sx
Normal file
101
lib/content/store.sx
Normal file
@@ -0,0 +1,101 @@
|
||||
;; 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))))
|
||||
Reference in New Issue
Block a user