;; lib/host/blog.sx — Blog domain on the host. Posts are content-on-sx documents ;; whose source of truth is the durable SX store (persist op-log on disk). Serving ;; GET // is FULLY DYNAMIC: the handler reads the post from the store and ;; renders it to HTML, per request — no in-memory view, no cached output. This is ;; possible because http-listen handlers now resolve per-request IO (the ;; cek_run_with_io kernel fix). The original strangler target (Quart blog ;; post_detail); published posts are world-visible, so this endpoint is ANONYMOUS. ;; ;; NOTE ON SPEED: content/html runs the interpreted Smalltalk-on-SX dispatch ;; (~2s for a tiny doc) because the JIT is not installed in this serving mode AND ;; currently miscompiles the Smalltalk evaluator's nested ASTs. Making the render ;; fast is a JIT-compiler fix (or a Smalltalk-interpreter optimisation), tracked ;; separately — it is NOT solved by caching the output. ;; Depends on lib/content/* (+ Smalltalk + persist preloads) + lib/dream/* + ;; lib/host/handler.sx. ;; Register content classes + render methods (idempotent); called at load below. (define host/blog-bootstrap! (fn () (begin (st-bootstrap-classes!) (content/bootstrap!)))) ;; ── store (durable source of truth, injectable) ───────────────────── (define host/blog-store (persist/open)) (define host/blog-use-store! (fn (b) (set! host/blog-store b))) ;; ── publish + lookup (per-request, against the store) ─────────────── ;; Publish a simple post (title heading + body paragraph): append its insert ops ;; to the durable store. `at` is a caller-supplied logical timestamp. (define host/blog-publish! (fn (slug title body at) (let ((hid (str slug "-h")) (tid (str slug "-body"))) (content/commit-all! host/blog-store slug (list (op-insert (mk-heading hid 1 title) nil) (op-insert (mk-text tid body) hid)) at)))) ;; Idempotent seed: publish only if the slug has no content yet (so a restart ;; replaying serve.sh doesn't append duplicate blocks to a persisted post). (define host/blog-seed! (fn (slug title body at) (when (= (content/count (content/head host/blog-store slug)) 0) (host/blog-publish! slug title body at)))) ;; Materialise the post from the store by replaying its op-log; nil if no content. ;; Reads the durable store via per-request IO (works inside the handler thread). (define host/blog-lookup (fn (slug) (let ((doc (content/head host/blog-store slug))) (if (> (content/count doc) 0) doc nil)))) ;; ── handler: GET // -> rendered HTML (200) or 404 ───────────── (define host/blog-post (fn (req) (let ((slug (dream-param req "slug"))) (let ((doc (host/blog-lookup slug))) (if doc (dream-html (content/html doc)) (dream-html-status 404 (str "Not found" "

404

No published post: " slug "

"))))))) ;; Anonymous read route. MUST be mounted LAST: the :slug pattern matches any ;; single-segment path, so domain routes (/feed, /health) take precedence. (define host/blog-routes (list (dream-get "/:slug" host/blog-post))) ;; Self-bootstrap at load (content modules are loaded before this one). (host/blog-bootstrap!)