content: id remapping / clone (clone.sx) + 10 tests (561/561)
Some checks failed
Test, Build, and Deploy / test-build-deploy (push) Failing after 45s
Some checks failed
Test, Build, and Deploy / test-build-deploy (push) Failing after 45s
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
This commit is contained in:
34
lib/content/clone.sx
Normal file
34
lib/content/clone.sx
Normal file
@@ -0,0 +1,34 @@
|
|||||||
|
;; content-on-sx — block id remapping / clone.
|
||||||
|
;;
|
||||||
|
;; Deep-rewrite every block id in the tree (descending into sections) by applying
|
||||||
|
;; a function. Enables collision-free composition: prefix one document's ids
|
||||||
|
;; before concatenating it with another. Immutable; content is unchanged, only
|
||||||
|
;; ids.
|
||||||
|
;;
|
||||||
|
;; Requires (loaded by harness): doc.sx, section.sx (section? /
|
||||||
|
;; section-children / section-with-children).
|
||||||
|
|
||||||
|
(define
|
||||||
|
block-remap-id
|
||||||
|
(fn
|
||||||
|
(b f)
|
||||||
|
(let
|
||||||
|
((nb (blk-set b "id" (f (blk-id b)))))
|
||||||
|
(if
|
||||||
|
(section? nb)
|
||||||
|
(section-with-children
|
||||||
|
nb
|
||||||
|
(map (fn (c) (block-remap-id c f)) (section-children nb)))
|
||||||
|
nb))))
|
||||||
|
|
||||||
|
(define
|
||||||
|
content/remap-ids
|
||||||
|
(fn
|
||||||
|
(doc f)
|
||||||
|
(doc-with-blocks
|
||||||
|
doc
|
||||||
|
(map (fn (b) (block-remap-id b f)) (doc-blocks doc)))))
|
||||||
|
|
||||||
|
(define
|
||||||
|
content/prefix-ids
|
||||||
|
(fn (doc prefix) (content/remap-ids doc (fn (id) (str prefix id)))))
|
||||||
@@ -15,7 +15,7 @@ if [ ! -x "$SX_SERVER" ]; then
|
|||||||
fi
|
fi
|
||||||
fi
|
fi
|
||||||
|
|
||||||
SUITES=(block doc render api meta page page-full markdown text section compose tree-edit stats table data wire validate store snapshot crdt crdt-store sync md-import md-doc fed)
|
SUITES=(block doc render api meta page page-full markdown text section compose tree-edit clone stats table data wire validate store snapshot crdt crdt-store sync md-import md-doc fed)
|
||||||
|
|
||||||
OUT_JSON="lib/content/scoreboard.json"
|
OUT_JSON="lib/content/scoreboard.json"
|
||||||
OUT_MD="lib/content/scoreboard.md"
|
OUT_MD="lib/content/scoreboard.md"
|
||||||
@@ -47,6 +47,7 @@ run_suite() {
|
|||||||
(load "lib/content/section.sx")
|
(load "lib/content/section.sx")
|
||||||
(load "lib/content/compose.sx")
|
(load "lib/content/compose.sx")
|
||||||
(load "lib/content/tree-edit.sx")
|
(load "lib/content/tree-edit.sx")
|
||||||
|
(load "lib/content/clone.sx")
|
||||||
(load "lib/content/stats.sx")
|
(load "lib/content/stats.sx")
|
||||||
(load "lib/content/table.sx")
|
(load "lib/content/table.sx")
|
||||||
(load "lib/content/data.sx")
|
(load "lib/content/data.sx")
|
||||||
|
|||||||
@@ -12,6 +12,7 @@
|
|||||||
"section": {"pass": 25, "fail": 0},
|
"section": {"pass": 25, "fail": 0},
|
||||||
"compose": {"pass": 17, "fail": 0},
|
"compose": {"pass": 17, "fail": 0},
|
||||||
"tree-edit": {"pass": 17, "fail": 0},
|
"tree-edit": {"pass": 17, "fail": 0},
|
||||||
|
"clone": {"pass": 10, "fail": 0},
|
||||||
"stats": {"pass": 17, "fail": 0},
|
"stats": {"pass": 17, "fail": 0},
|
||||||
"table": {"pass": 15, "fail": 0},
|
"table": {"pass": 15, "fail": 0},
|
||||||
"data": {"pass": 21, "fail": 0},
|
"data": {"pass": 21, "fail": 0},
|
||||||
@@ -26,7 +27,7 @@
|
|||||||
"md-doc": {"pass": 12, "fail": 0},
|
"md-doc": {"pass": 12, "fail": 0},
|
||||||
"fed": {"pass": 20, "fail": 0}
|
"fed": {"pass": 20, "fail": 0}
|
||||||
},
|
},
|
||||||
"total_pass": 551,
|
"total_pass": 561,
|
||||||
"total_fail": 0,
|
"total_fail": 0,
|
||||||
"total": 551
|
"total": 561
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -16,6 +16,7 @@ _Generated by `lib/content/conformance.sh`_
|
|||||||
| section | 25 | 0 | 25 |
|
| section | 25 | 0 | 25 |
|
||||||
| compose | 17 | 0 | 17 |
|
| compose | 17 | 0 | 17 |
|
||||||
| tree-edit | 17 | 0 | 17 |
|
| tree-edit | 17 | 0 | 17 |
|
||||||
|
| clone | 10 | 0 | 10 |
|
||||||
| stats | 17 | 0 | 17 |
|
| stats | 17 | 0 | 17 |
|
||||||
| table | 15 | 0 | 15 |
|
| table | 15 | 0 | 15 |
|
||||||
| data | 21 | 0 | 21 |
|
| data | 21 | 0 | 21 |
|
||||||
@@ -29,4 +30,4 @@ _Generated by `lib/content/conformance.sh`_
|
|||||||
| md-import | 38 | 0 | 38 |
|
| md-import | 38 | 0 | 38 |
|
||||||
| md-doc | 12 | 0 | 12 |
|
| md-doc | 12 | 0 | 12 |
|
||||||
| fed | 20 | 0 | 20 |
|
| fed | 20 | 0 | 20 |
|
||||||
| **Total** | **551** | **0** | **551** |
|
| **Total** | **561** | **0** | **561** |
|
||||||
|
|||||||
55
lib/content/tests/clone.sx
Normal file
55
lib/content/tests/clone.sx
Normal file
@@ -0,0 +1,55 @@
|
|||||||
|
;; Extension — block id remapping / clone.
|
||||||
|
|
||||||
|
(st-bootstrap-classes!)
|
||||||
|
(content/bootstrap!)
|
||||||
|
(content-bootstrap-section!)
|
||||||
|
|
||||||
|
(define
|
||||||
|
d
|
||||||
|
(doc-append
|
||||||
|
(doc-append (doc-empty "d") (mk-heading "h" 1 "Title"))
|
||||||
|
(mk-section "s" (list (mk-text "a" "A") (mk-text "b" "B")))))
|
||||||
|
|
||||||
|
;; ── prefix-ids rewrites every id in the tree ──
|
||||||
|
(define p (content/prefix-ids d "x-"))
|
||||||
|
(content-test "prefix top-level ids" (doc-ids p) (list "x-h" "x-s"))
|
||||||
|
(content-test
|
||||||
|
"prefix tree-ids"
|
||||||
|
(doc-tree-ids p)
|
||||||
|
(list "x-h" "x-s" "x-a" "x-b"))
|
||||||
|
(content-test "prefix immutable" (doc-tree-ids d) (list "h" "s" "a" "b"))
|
||||||
|
(content-test "prefix preserves content" (asHTML p) (asHTML d))
|
||||||
|
(content-test
|
||||||
|
"prefix preserves nested content"
|
||||||
|
(str (blk-send (doc-deep-find p "x-a") "text"))
|
||||||
|
"A")
|
||||||
|
|
||||||
|
;; ── custom remap fn ──
|
||||||
|
(define u (content/remap-ids d (fn (id) (str id "!"))))
|
||||||
|
(content-test "remap suffix" (doc-tree-ids u) (list "h!" "s!" "a!" "b!"))
|
||||||
|
|
||||||
|
;; ── collision-free composition ──
|
||||||
|
(define
|
||||||
|
d2
|
||||||
|
(doc-append (doc-empty "d2") (mk-heading "h" 2 "Other")))
|
||||||
|
(define
|
||||||
|
combined
|
||||||
|
(content/concat
|
||||||
|
(content/prefix-ids d "left-")
|
||||||
|
(content/prefix-ids d2 "right-")))
|
||||||
|
(content-test
|
||||||
|
"combined ids unique"
|
||||||
|
(doc-tree-ids combined)
|
||||||
|
(list "left-h" "left-s" "left-a" "left-b" "right-h"))
|
||||||
|
(content-test "combined validates" (content/valid? combined) true)
|
||||||
|
;; without prefixing, the shared id "h" collides
|
||||||
|
(content-test
|
||||||
|
"unprefixed collides"
|
||||||
|
(content/valid? (content/concat d d2))
|
||||||
|
false)
|
||||||
|
|
||||||
|
;; ── render of combined ──
|
||||||
|
(content-test
|
||||||
|
"combined render"
|
||||||
|
(asHTML combined)
|
||||||
|
"<h1>Title</h1><section><p>A</p><p>B</p></section><h2>Other</h2>")
|
||||||
@@ -19,7 +19,7 @@ injected adapter, not core.
|
|||||||
|
|
||||||
## Status (rolling)
|
## Status (rolling)
|
||||||
|
|
||||||
`bash lib/content/conformance.sh` → **551/551** (Phases 1–4 COMPLETE + extensions: HTML/SX escaping, Markdown render + import/export incl. tables & frontmatter (full round-trip), CRDT replication, tree-aware validation, snapshot cache, doc metadata, plain-text render, nested block trees + deep tree editing, doc stats, table block, HTML page wrapper + SEO page, doc composition, portable data + wire serialization)
|
`bash lib/content/conformance.sh` → **561/561** (Phases 1–4 COMPLETE + extensions: HTML/SX escaping, Markdown render + import/export incl. tables & frontmatter (full round-trip), CRDT replication, tree-aware validation, snapshot cache, doc metadata, plain-text render, nested block trees + deep tree editing, doc stats, table block, HTML page wrapper + SEO page, doc composition + id-remap, portable data + wire serialization)
|
||||||
|
|
||||||
## Ground rules
|
## Ground rules
|
||||||
|
|
||||||
@@ -93,11 +93,18 @@ lib/content/api.sx ── (content/edit) (content/render) (content/history) ─
|
|||||||
- [x] SEO page (`page-full.sx`: content/page-full, lang + meta description from excerpt)
|
- [x] SEO page (`page-full.sx`: content/page-full, lang + meta description from excerpt)
|
||||||
- [x] document composition (`compose.sx`: concat/prepend/concat-all/wrap-section)
|
- [x] document composition (`compose.sx`: concat/prepend/concat-all/wrap-section)
|
||||||
- [x] deep tree editing (`tree-edit.sx`: doc-deep-update/replace/delete/insert-into)
|
- [x] deep tree editing (`tree-edit.sx`: doc-deep-update/replace/delete/insert-into)
|
||||||
|
- [x] id remapping / clone (`clone.sx`: content/remap-ids + prefix-ids, collision-free compose)
|
||||||
- [x] portable data serialization (`data.sx`: content/to-data + from-data, round-trips tree)
|
- [x] portable data serialization (`data.sx`: content/to-data + from-data, round-trips tree)
|
||||||
- [x] wire serialization (`wire.sx`: content/to-wire + from-wire, SX-text on the wire)
|
- [x] wire serialization (`wire.sx`: content/to-wire + from-wire, SX-text on the wire)
|
||||||
|
|
||||||
## Progress log
|
## Progress log
|
||||||
|
|
||||||
|
- 2026-06-07 — Extension: id remapping / clone (`clone.sx`).
|
||||||
|
`content/remap-ids` deep-rewrites every block id across the tree (sections
|
||||||
|
recurse) via a function; `content/prefix-ids` prefixes them. Enables
|
||||||
|
collision-free composition (prefix each doc before concat → validates clean,
|
||||||
|
where the unprefixed concat has duplicate ids). Content unchanged, only ids;
|
||||||
|
immutable. 10 tests; suite 561/561.
|
||||||
- 2026-06-07 — Extension: deep tree editing (`tree-edit.sx`). `doc-deep-update`
|
- 2026-06-07 — Extension: deep tree editing (`tree-edit.sx`). `doc-deep-update`
|
||||||
/ `doc-deep-replace` / `doc-deep-delete` / `doc-deep-insert-into` mutate blocks
|
/ `doc-deep-replace` / `doc-deep-delete` / `doc-deep-insert-into` mutate blocks
|
||||||
anywhere in the nested tree (descending into CtSection children), completing
|
anywhere in the nested tree (descending into CtSection children), completing
|
||||||
|
|||||||
Reference in New Issue
Block a user