From 90136f3a996baaa260da2a12d1b91a75c38773a4 Mon Sep 17 00:00:00 2001 From: giles Date: Sun, 7 Jun 2026 03:18:09 +0000 Subject: [PATCH] content: on-the-wire serialization (wire.sx) + 11 tests (534/534) Co-Authored-By: Claude Opus 4.8 (1M context) --- lib/content/conformance.sh | 3 +- lib/content/scoreboard.json | 5 +-- lib/content/scoreboard.md | 3 +- lib/content/tests/wire.sx | 63 +++++++++++++++++++++++++++++++++++++ lib/content/wire.sx | 14 +++++++++ plans/content-on-sx.md | 9 +++++- 6 files changed, 92 insertions(+), 5 deletions(-) create mode 100644 lib/content/tests/wire.sx create mode 100644 lib/content/wire.sx diff --git a/lib/content/conformance.sh b/lib/content/conformance.sh index 7158b62b..4c4ab5b5 100755 --- a/lib/content/conformance.sh +++ b/lib/content/conformance.sh @@ -15,7 +15,7 @@ if [ ! -x "$SX_SERVER" ]; then fi fi -SUITES=(block doc render api meta page page-full markdown text section compose stats table data 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 stats table data wire validate store snapshot crdt crdt-store sync md-import md-doc fed) OUT_JSON="lib/content/scoreboard.json" OUT_MD="lib/content/scoreboard.md" @@ -49,6 +49,7 @@ run_suite() { (load "lib/content/stats.sx") (load "lib/content/table.sx") (load "lib/content/data.sx") +(load "lib/content/wire.sx") (load "lib/content/page.sx") (load "lib/content/page-full.sx") (load "lib/content/markdown.sx") diff --git a/lib/content/scoreboard.json b/lib/content/scoreboard.json index adee7c9d..6a90f654 100644 --- a/lib/content/scoreboard.json +++ b/lib/content/scoreboard.json @@ -14,6 +14,7 @@ "stats": {"pass": 17, "fail": 0}, "table": {"pass": 15, "fail": 0}, "data": {"pass": 21, "fail": 0}, + "wire": {"pass": 11, "fail": 0}, "validate": {"pass": 23, "fail": 0}, "store": {"pass": 29, "fail": 0}, "snapshot": {"pass": 20, "fail": 0}, @@ -24,7 +25,7 @@ "md-doc": {"pass": 12, "fail": 0}, "fed": {"pass": 20, "fail": 0} }, - "total_pass": 523, + "total_pass": 534, "total_fail": 0, - "total": 523 + "total": 534 } diff --git a/lib/content/scoreboard.md b/lib/content/scoreboard.md index f144b600..9497efba 100644 --- a/lib/content/scoreboard.md +++ b/lib/content/scoreboard.md @@ -18,6 +18,7 @@ _Generated by `lib/content/conformance.sh`_ | stats | 17 | 0 | 17 | | table | 15 | 0 | 15 | | data | 21 | 0 | 21 | +| wire | 11 | 0 | 11 | | validate | 23 | 0 | 23 | | store | 29 | 0 | 29 | | snapshot | 20 | 0 | 20 | @@ -27,4 +28,4 @@ _Generated by `lib/content/conformance.sh`_ | md-import | 38 | 0 | 38 | | md-doc | 12 | 0 | 12 | | fed | 20 | 0 | 20 | -| **Total** | **523** | **0** | **523** | +| **Total** | **534** | **0** | **534** | diff --git a/lib/content/tests/wire.sx b/lib/content/tests/wire.sx new file mode 100644 index 00000000..afa289f0 --- /dev/null +++ b/lib/content/tests/wire.sx @@ -0,0 +1,63 @@ +;; Extension — on-the-wire serialization (to-wire / from-wire). + +(st-bootstrap-classes!) +(content/bootstrap!) +(content-bootstrap-text!) +(content-bootstrap-section!) +(content-bootstrap-table!) + +(define + d + (doc-with-meta + (doc-append + (doc-append (doc-empty "post") (mk-heading "h" 1 "Title")) + (mk-text "p" "Body text")) + {:title "T" :tags (list "x" "y")})) + +;; ── to-wire produces a string ── +(content-test "to-wire is string" (string? (content/to-wire d)) true) + +;; ── parse(to-wire) == data form ── +(content-test + "wire parses to data" + (parse (content/to-wire d)) + (content/to-data d)) + +;; ── round-trip preserves everything ── +(define rt (content/wire-round-trip d)) +(content-test "rt id" (doc-id rt) "post") +(content-test "rt title" (doc-title rt) "T") +(content-test "rt tags" (doc-tags rt) (list "x" "y")) +(content-test "rt ids" (doc-ids rt) (list "h" "p")) +(content-test "rt render" (asHTML rt) (asHTML d)) + +;; ── nested + table survive the wire ── +(define + dn + (doc-append + (doc-append + (doc-empty "d") + (mk-section "s" (list (mk-text "a" "deep")))) + (mk-table "t" (list "A") (list (list "1"))))) +(content-test + "wire nested render" + (asHTML (content/wire-round-trip dn)) + (asHTML dn)) +(content-test + "wire nested tree-ids" + (doc-tree-ids (content/wire-round-trip dn)) + (doc-tree-ids dn)) + +;; ── empty doc ── +(content-test + "wire empty" + (doc-ids (content/from-wire (content/to-wire (doc-empty "e")))) + (list)) + +;; ── from-wire of an externally-built wire string ── +(content-test + "from-wire external" + (asHTML + (content/from-wire + "{:id \"x\" :blocks ({:id \"h\" :type \"heading\" :fields {:level 2 :text \"Hi\"}})}")) + "

Hi

") diff --git a/lib/content/wire.sx b/lib/content/wire.sx new file mode 100644 index 00000000..974b60b1 --- /dev/null +++ b/lib/content/wire.sx @@ -0,0 +1,14 @@ +;; content-on-sx — on-the-wire serialization. +;; +;; content/to-wire serialises a document to a transmittable SX-text string (via +;; the data form + the SX serializer); content/from-wire parses it back into a +;; live document. This is the format to persist a whole document or send it over +;; HTTP / federation, distinct from the per-op persist log. +;; +;; Requires (loaded by harness): data.sx (content/to-data / content/from-data). + +(define content/to-wire (fn (doc) (serialize (content/to-data doc)))) +(define content/from-wire (fn (s) (content/from-data (parse s)))) +(define + content/wire-round-trip + (fn (doc) (content/from-wire (content/to-wire doc)))) diff --git a/plans/content-on-sx.md b/plans/content-on-sx.md index 49326b10..b11f284b 100644 --- a/plans/content-on-sx.md +++ b/plans/content-on-sx.md @@ -19,7 +19,7 @@ injected adapter, not core. ## Status (rolling) -`bash lib/content/conformance.sh` → **523/523** (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, doc stats, table block, HTML page wrapper + SEO page, doc composition, portable data serialization) +`bash lib/content/conformance.sh` → **534/534** (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, doc stats, table block, HTML page wrapper + SEO page, doc composition, portable data + wire serialization) ## Ground rules @@ -93,9 +93,16 @@ 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] document composition (`compose.sx`: concat/prepend/concat-all/wrap-section) - [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) ## Progress log +- 2026-06-07 — Extension: on-the-wire serialization (`wire.sx`). + `content/to-wire` serialises a document to a transmittable SX-text string (data + form + SX serializer); `content/from-wire` parses it back into a live document. + The whole-document format for persistence / HTTP / federation (distinct from + the per-op persist log); round-trips nested trees + tables; reads externally + authored wire strings. 11 tests; suite 534/534. - 2026-06-07 — Extension: portable data serialization (`data.sx`). `content/to-data` converts a document to plain SX data (`{:id :title :slug :tags :blocks [{:id :type :fields}]}`, sections recursing);