Build tooling: updated OCaml bootstrapper, compile-modules, bundle.sh, sx-build-all. WASM browser: rebuilt sx_browser.bc.js/wasm, sx-platform-2.js, .sxbc bytecode files. CSSX/Tailwind: reworked cssx.sx templates and tw-layout, added tw-type support. Content: refreshed essays, plans, geography, reactive islands, docs, demos, handlers. New tools: bisect_sxbc.sh, test-spa.js, render-trace.sx, morph playwright spec. Tests: added test-match.sx, test-examples.sx, updated test-tw.sx and web tests. Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
99 lines
7.6 KiB
Plaintext
99 lines
7.6 KiB
Plaintext
;; Protocol documentation pages — fully self-contained
|
|
|
|
(defcomp ~protocols/wire-format-content ()
|
|
(~docs/page :title "Wire Format"
|
|
(~docs/section :title "The text/sx content type" :id "content-type"
|
|
(p (~tw :tokens "text-stone-600")
|
|
"sx responses use content type text/sx. The body is s-expression source code. The client parses and evaluates it, then renders the result into the DOM.")
|
|
(~docs/code :src (highlight "HTTP/1.1 200 OK\nContent-Type: text/sx\nSX-Css-Hash: a1b2c3d4\n\n(div :class \"p-4\"\n (~card :title \"Hello\"))" "bash")))
|
|
(~docs/section :title "Request lifecycle" :id "lifecycle"
|
|
(p (~tw :tokens "text-stone-600")
|
|
"1. User interacts with an element that has sx-get/sx-post/etc.")
|
|
(p (~tw :tokens "text-stone-600")
|
|
"2. sx.js fires sx:beforeRequest, then sends the HTTP request with SX-Request: true header.")
|
|
(p (~tw :tokens "text-stone-600")
|
|
"3. Server builds s-expression tree, scans CSS classes, prepends missing component definitions.")
|
|
(p (~tw :tokens "text-stone-600")
|
|
"4. Client receives text/sx response, parses it, evaluates it, renders to DOM.")
|
|
(p (~tw :tokens "text-stone-600")
|
|
"5. sx.js fires sx:afterSwap and sx:afterSettle.")
|
|
(p (~tw :tokens "text-stone-600")
|
|
"6. Any sx-swap-oob elements are swapped into their targets elsewhere in the DOM."))
|
|
(~docs/section :title "Component definitions" :id "components"
|
|
(p (~tw :tokens "text-stone-600")
|
|
"On full page loads, component definitions are in <script type=\"text/sx\" data-components>. On subsequent sx requests, missing definitions are prepended to the response body. The client caches definitions in localStorage keyed by a content hash."))))
|
|
|
|
(defcomp ~protocols/fragments-content ()
|
|
(~docs/page :title "Cross-Service Fragments"
|
|
(~docs/section :title "Fragment protocol" :id "protocol"
|
|
(p (~tw :tokens "text-stone-600")
|
|
"Rose Ash runs as independent microservices. Each service can expose HTML or sx fragments that other services compose into their pages. Fragment endpoints return text/sx or text/html.")
|
|
(p (~tw :tokens "text-stone-600")
|
|
"The frag resolver is an I/O primitive in the render tree:")
|
|
(~docs/code :src (highlight "(frag \"blog\" \"link-card\" :slug \"hello\")" "lisp")))
|
|
(~docs/section :title "SxExpr wrapping" :id "wrapping"
|
|
(p (~tw :tokens "text-stone-600")
|
|
"When a fragment returns text/sx, the response is wrapped in an SxExpr and embedded directly in the render tree. When it returns text/html, it's wrapped in a ~rich-text component that inserts the HTML via raw!. This allows transparent composition across service boundaries."))
|
|
(~docs/section :title "fetch_fragments()" :id "fetch"
|
|
(p (~tw :tokens "text-stone-600")
|
|
"The Python helper fetch_fragments() fetches multiple fragments in parallel via asyncio.gather(). Fragments are cached in Redis with short TTLs. Each fragment request is HMAC-signed for authentication."))))
|
|
|
|
(defcomp ~protocols/resolver-io-content ()
|
|
(~docs/page :title "Resolver I/O"
|
|
(~docs/section :title "Async I/O primitives" :id "primitives"
|
|
(p (~tw :tokens "text-stone-600")
|
|
"The sx resolver identifies I/O nodes in the render tree, groups them, executes them in parallel via asyncio.gather(), and substitutes results back in.")
|
|
(p (~tw :tokens "text-stone-600")
|
|
"I/O primitives:")
|
|
(ul (~tw :tokens "space-y-2 text-stone-600")
|
|
(li (span (~tw :tokens "font-mono text-violet-700") "frag") " — fetch a cross-service fragment")
|
|
(li (span (~tw :tokens "font-mono text-violet-700") "query") " — read data from another service")
|
|
(li (span (~tw :tokens "font-mono text-violet-700") "action") " — execute a write on another service")
|
|
(li (span (~tw :tokens "font-mono text-violet-700") "current-user") " — resolve the current authenticated user")))
|
|
(~docs/section :title "Execution model" :id "execution"
|
|
(p (~tw :tokens "text-stone-600")
|
|
"The render tree is walked to find I/O nodes. All nodes at the same depth are gathered and executed in parallel. Results replace the I/O nodes in the tree. The walk continues until no more I/O nodes are found. This typically completes in 1-2 passes."))))
|
|
|
|
(defcomp ~protocols/internal-services-content ()
|
|
(~docs/page :title "Internal Services"
|
|
(~docs/note
|
|
(p "Honest note: the internal service protocol is JSON, not sx. Sx is the composition layer on top. The protocols below are the plumbing underneath."))
|
|
(~docs/section :title "HMAC-signed HTTP" :id "hmac"
|
|
(p (~tw :tokens "text-stone-600")
|
|
"Services communicate via HMAC-signed HTTP requests with short timeouts:")
|
|
(ul (~tw :tokens "space-y-2 text-stone-600 font-mono text-sm")
|
|
(li "GET /internal/data/{query} — read data (3s timeout)")
|
|
(li "POST /internal/actions/{action} — execute write (5s timeout)")
|
|
(li "POST /internal/inbox — ActivityPub-shaped event delivery")))
|
|
(~docs/section :title "fetch_data / call_action" :id "fetch"
|
|
(p (~tw :tokens "text-stone-600")
|
|
"Python helpers fetch_data() and call_action() handle HMAC signing, serialization, and error handling. They resolve service URLs from environment variables (INTERNAL_URL_BLOG, etc) and fall back to public URLs in development."))))
|
|
|
|
(defcomp ~protocols/activitypub-content ()
|
|
(~docs/page :title "ActivityPub"
|
|
(~docs/note
|
|
(p "Honest note: ActivityPub wire format is JSON-LD, not sx. This documents how AP integrates with the sx rendering layer."))
|
|
(~docs/section :title "AP activities" :id "activities"
|
|
(p (~tw :tokens "text-stone-600")
|
|
"Rose Ash services communicate cross-domain writes via ActivityPub-shaped activities. Each service has a virtual actor. Activities are JSON-LD objects sent to /internal/inbox endpoints. RSA signatures authenticate the sender."))
|
|
(~docs/section :title "Event bus" :id "bus"
|
|
(p (~tw :tokens "text-stone-600")
|
|
"The event bus dispatches activities to registered handlers. Handlers are async functions that process the activity and may trigger side effects. The bus runs as a background processor in each service."))))
|
|
|
|
(defcomp ~protocols/future-content ()
|
|
(~docs/page :title "Future Possibilities"
|
|
(~docs/note
|
|
(p "This page is speculative. Nothing here is implemented. It documents ideas that may or may not happen."))
|
|
(~docs/section :title "Custom protocol schemes" :id "schemes"
|
|
(p (~tw :tokens "text-stone-600")
|
|
"sx:// and sxs:// as custom URI schemes for content addressing or deep linking. An sx:// URI could resolve to an sx expression from a federated registry. This is technically feasible but practically unnecessary for a single-site deployment."))
|
|
(~docs/section :title "Sx as AP serialization" :id "ap-sx"
|
|
(p (~tw :tokens "text-stone-600")
|
|
"ActivityPub objects could be serialized as s-expressions instead of JSON-LD. S-expressions are more compact and easier to parse. The practical barrier: the entire AP ecosystem expects JSON-LD."))
|
|
(~docs/section :title "Sx-native federation" :id "federation"
|
|
(p (~tw :tokens "text-stone-600")
|
|
"Federated services could exchange sx fragments directly — render a remote user's profile card by fetching its sx source from their server. This requires trust and standardization that doesn't exist yet."))
|
|
(~docs/section :title "Realistic assessment" :id "realistic"
|
|
(p (~tw :tokens "text-stone-600")
|
|
"The most likely near-term improvement is sx:// deep linking for client-side component resolution. Everything else requires ecosystem adoption that one project can't drive alone."))))
|