content: by-id ops (update/delete) act tree-wide — fixes op-log no-op on nested blocks + 4 tests (750/750)
Some checks failed
Test, Build, and Deploy / test-build-deploy (push) Failing after 14m45s
Some checks failed
Test, Build, and Deploy / test-build-deploy (push) Failing after 14m45s
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
This commit is contained in:
@@ -5,14 +5,18 @@
|
||||
;; and returns a NEW document — the input is never mutated, so any version is the
|
||||
;; head of an op stream (replay-friendly for persist + CRDT merge).
|
||||
;;
|
||||
;; CtDoc also carries optional metadata (title/slug/tags) — see meta.sx for the
|
||||
;; ergonomic API; they default nil and do not affect block operations.
|
||||
;; By-id ops (update/delete) are TREE-WIDE: they descend into any block carrying
|
||||
;; a `children` list (i.e. sections), since ids are unique across the tree. This
|
||||
;; keeps the persist op-log and content/edit correct for nested documents.
|
||||
;; insert/move are positional and act at the top level.
|
||||
;;
|
||||
;; CtDoc also carries optional metadata (title/slug/tags) — see meta.sx.
|
||||
;;
|
||||
;; Op shapes (data, not objects — they are the persist event payload):
|
||||
;; {:op "insert" :block <blk> :after <id|nil>} ; after nil = prepend
|
||||
;; {:op "update" :id <id> :field <name> :value <v>}
|
||||
;; {:op "move" :id <id> :index <n>}
|
||||
;; {:op "delete" :id <id>}
|
||||
;; {:op "insert" :block <blk> :after <id|nil>} ; after nil = prepend (top level)
|
||||
;; {:op "update" :id <id> :field <name> :value <v>} ; tree-wide by id
|
||||
;; {:op "move" :id <id> :index <n>} ; top level
|
||||
;; {:op "delete" :id <id>} ; tree-wide by id
|
||||
|
||||
(define
|
||||
content-bootstrap-doc!
|
||||
@@ -76,17 +80,38 @@
|
||||
(first blocks)
|
||||
(ct-insert-at (rest blocks) (- i 1) x))))))
|
||||
|
||||
;; tree-wide remove by id: drop matches at this level, recurse into children
|
||||
;; (blocks carrying a `children` list, i.e. sections).
|
||||
(define
|
||||
ct-remove-id
|
||||
(fn
|
||||
(blocks id)
|
||||
(filter (fn (b) (if (= (blk-id b) id) false true)) blocks)))
|
||||
(map
|
||||
(fn
|
||||
(b)
|
||||
(let
|
||||
((ch (st-iv-get b "children")))
|
||||
(if (list? ch) (st-iv-set! b "children" (ct-remove-id ch id)) b)))
|
||||
(filter (fn (b) (if (= (blk-id b) id) false true)) blocks))))
|
||||
|
||||
;; tree-wide replace by id: apply f to the match wherever it sits in the tree.
|
||||
(define
|
||||
ct-replace-id
|
||||
(fn
|
||||
(blocks id f)
|
||||
(map (fn (b) (if (= (blk-id b) id) (f b) b)) blocks)))
|
||||
(map
|
||||
(fn
|
||||
(b)
|
||||
(if
|
||||
(= (blk-id b) id)
|
||||
(f b)
|
||||
(let
|
||||
((ch (st-iv-get b "children")))
|
||||
(if
|
||||
(list? ch)
|
||||
(st-iv-set! b "children" (ct-replace-id ch id f))
|
||||
b))))
|
||||
blocks)))
|
||||
|
||||
;; ── query ──
|
||||
(define doc-index-of (fn (doc id) (ct-index-of (doc-blocks doc) id)))
|
||||
@@ -164,7 +189,7 @@
|
||||
;; ── op constructors (data payload, reused by persist op log) ──
|
||||
(define op-insert (fn (block after) {:after after :op "insert" :block block}))
|
||||
|
||||
(define op-update (fn (id field value) {:field field :id id :op "update" :value value}))
|
||||
(define op-update (fn (id field value) {:id id :field field :op "update" :value value}))
|
||||
|
||||
(define op-move (fn (id index) {:id id :op "move" :index index}))
|
||||
|
||||
|
||||
@@ -31,7 +31,7 @@
|
||||
"data": {"pass": 25, "fail": 0},
|
||||
"wire": {"pass": 11, "fail": 0},
|
||||
"validate": {"pass": 23, "fail": 0},
|
||||
"store": {"pass": 33, "fail": 0},
|
||||
"store": {"pass": 37, "fail": 0},
|
||||
"snapshot": {"pass": 20, "fail": 0},
|
||||
"crdt": {"pass": 34, "fail": 0},
|
||||
"crdt-tree": {"pass": 21, "fail": 0},
|
||||
@@ -42,7 +42,7 @@
|
||||
"md-doc": {"pass": 12, "fail": 0},
|
||||
"fed": {"pass": 20, "fail": 0}
|
||||
},
|
||||
"total_pass": 746,
|
||||
"total_pass": 750,
|
||||
"total_fail": 0,
|
||||
"total": 746
|
||||
"total": 750
|
||||
}
|
||||
|
||||
@@ -35,7 +35,7 @@ _Generated by `lib/content/conformance.sh`_
|
||||
| data | 25 | 0 | 25 |
|
||||
| wire | 11 | 0 | 11 |
|
||||
| validate | 23 | 0 | 23 |
|
||||
| store | 33 | 0 | 33 |
|
||||
| store | 37 | 0 | 37 |
|
||||
| snapshot | 20 | 0 | 20 |
|
||||
| crdt | 34 | 0 | 34 |
|
||||
| crdt-tree | 21 | 0 | 21 |
|
||||
@@ -45,4 +45,4 @@ _Generated by `lib/content/conformance.sh`_
|
||||
| md-import | 38 | 0 | 38 |
|
||||
| md-doc | 12 | 0 | 12 |
|
||||
| fed | 20 | 0 | 20 |
|
||||
| **Total** | **746** | **0** | **746** |
|
||||
| **Total** | **750** | **0** | **750** |
|
||||
|
||||
@@ -151,3 +151,30 @@
|
||||
"op-log media type"
|
||||
(blk-type (doc-find (content/head B3 "rich") "v"))
|
||||
"media")
|
||||
|
||||
;; ── op-log update/delete reach NESTED blocks (tree-wide by id) ──
|
||||
(content-bootstrap-section!)
|
||||
(define B4 (persist/open))
|
||||
(content/commit!
|
||||
B4
|
||||
"nest"
|
||||
(op-insert (mk-section "sec" (list (mk-text "n" "orig"))) nil)
|
||||
1)
|
||||
(content/commit! B4 "nest" (op-update "n" "text" "edited") 2)
|
||||
(content-test
|
||||
"op-log nested update"
|
||||
(str (blk-send (doc-deep-find (content/head B4 "nest") "n") "text"))
|
||||
"edited")
|
||||
(content-test
|
||||
"op-log nested update tree intact"
|
||||
(doc-tree-ids (content/head B4 "nest"))
|
||||
(list "sec" "n"))
|
||||
(content/commit! B4 "nest" (op-delete "n") 3)
|
||||
(content-test
|
||||
"op-log nested delete"
|
||||
(doc-tree-ids (content/head B4 "nest"))
|
||||
(list "sec"))
|
||||
(content-test
|
||||
"op-log nested delete via content/at seq2"
|
||||
(doc-tree-ids (content/at B4 "nest" 2))
|
||||
(list "sec" "n"))
|
||||
|
||||
Reference in New Issue
Block a user