engine: boosted forms post text/sx, not urlencoded (SX-native write wire)
Some checks failed
Test, Build, and Deploy / test-build-deploy (push) Failing after 34s
Some checks failed
Test, Build, and Deploy / test-build-deploy (push) Failing after 34s
build-request-body's POST-form branch now serialises the form fields to a text/sx body via the serialize primitive (content-type text/sx), instead of FormData -> URLSearchParams -> urlencoded. A hydrated page posts SX; the host reads it via host/sx-body / host/field (the server already accepts both — urlencoded stays the no-engine / login-bootstrap fallback). Recompiled the web stack -> .sxbc. Verified client-agnostically (no DOM, the user's preference): a new sxtp suite test proves the wire contract serialize(engine) <-> host/sx-body(server) round-trips a field dict losslessly, INCLUDING sx_content full of quotes/parens that would break a naive encoder, plus host/field's content-type discrimination + urlencoded fallback (sxtp 43/43). The DOM field-read (dom-query-all + .value) is the one irreducibly- browser bit — left to a targeted Playwright smoke. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
This commit is contained in:
@@ -119,6 +119,29 @@
|
|||||||
(dream-status (sxtp/to-dream (sxtp/forbidden "no")))
|
(dream-status (sxtp/to-dream (sxtp/forbidden "no")))
|
||||||
403)
|
403)
|
||||||
|
|
||||||
|
;; ── engine<->server write wire: serialize (engine) <-> host/sx-body (server) ──
|
||||||
|
;; A boosted form posts (serialize {field->value}) as text/sx; the server reads it
|
||||||
|
;; back with host/sx-body. This is the SX write wire, verified with NO DOM (client-
|
||||||
|
;; agnostic): what the engine's serialize emits, host/sx-body must parse back
|
||||||
|
;; losslessly — including sx_content full of the quotes/parens that would break a
|
||||||
|
;; naive encoder. (The server side is what conformance can prove; the DOM field-read
|
||||||
|
;; is the one irreducibly-browser bit, left to a Playwright smoke.)
|
||||||
|
(define host-sx-wire-content "(article (h1 \"Title\") (p \"He said \\\"hi\\\" (x)\"))")
|
||||||
|
(define host-sx-wire-req
|
||||||
|
(dream-request "POST" "/x" {:content-type "text/sx"}
|
||||||
|
(serialize {:title "Hi there" :sx_content host-sx-wire-content :status "published"})))
|
||||||
|
(host-sx-test "sx-body round-trips a serialized field dict"
|
||||||
|
(get (host/sx-body host-sx-wire-req) "title") "Hi there")
|
||||||
|
(host-sx-test "sx-body preserves quoted/parenthesised sx_content losslessly"
|
||||||
|
(get (host/sx-body host-sx-wire-req) "sx_content") host-sx-wire-content)
|
||||||
|
(host-sx-test "field reads a text/sx body by content-type"
|
||||||
|
(host/field host-sx-wire-req "status") "published")
|
||||||
|
(host-sx-test "field falls back to urlencoded form (the no-engine path)"
|
||||||
|
(host/field (dream-request "POST" "/x"
|
||||||
|
{:content-type "application/x-www-form-urlencoded"}
|
||||||
|
"title=From+Form&status=draft") "title")
|
||||||
|
"From Form")
|
||||||
|
|
||||||
(define
|
(define
|
||||||
host-sx-tests-run!
|
host-sx-tests-run!
|
||||||
(fn
|
(fn
|
||||||
|
|||||||
@@ -269,16 +269,28 @@
|
|||||||
(let
|
(let
|
||||||
((fd (host-new "FormData" el)))
|
((fd (host-new "FormData" el)))
|
||||||
(dict "url" url "body" fd "content-type" nil))
|
(dict "url" url "body" fd "content-type" nil))
|
||||||
|
;; SX-native wire: serialise the form fields to a text/sx body
|
||||||
|
;; (the host reads it via host/sx-body / host/field). A hydrated
|
||||||
|
;; page posts SX, not urlencoded; the server still accepts
|
||||||
|
;; urlencoded for the no-engine fallback. See plans/
|
||||||
|
;; relations-as-posts.md ("SX all the way out").
|
||||||
(let
|
(let
|
||||||
((fd (host-new "FormData" el))
|
((payload
|
||||||
(params (host-new "URLSearchParams" fd)))
|
(reduce
|
||||||
|
(fn (acc f)
|
||||||
|
(let ((nm (dom-get-attr f "name")))
|
||||||
|
(if (and nm (not (= nm "")))
|
||||||
|
(assoc acc nm (or (host-get f "value") ""))
|
||||||
|
acc)))
|
||||||
|
(dict)
|
||||||
|
(dom-query-all el "input, textarea, select"))))
|
||||||
(dict
|
(dict
|
||||||
"url"
|
"url"
|
||||||
url
|
url
|
||||||
"body"
|
"body"
|
||||||
(host-call params "toString")
|
(serialize payload)
|
||||||
"content-type"
|
"content-type"
|
||||||
"application/x-www-form-urlencoded"))))
|
"text/sx; charset=utf-8"))))
|
||||||
(dict "url" url "body" nil "content-type" nil))))))
|
(dict "url" url "body" nil "content-type" nil))))))
|
||||||
(define abort-previous-target (fn (el) nil))
|
(define abort-previous-target (fn (el) nil))
|
||||||
(define abort-previous (fn (el) nil))
|
(define abort-previous (fn (el) nil))
|
||||||
|
|||||||
File diff suppressed because one or more lines are too long
@@ -269,16 +269,28 @@
|
|||||||
(let
|
(let
|
||||||
((fd (host-new "FormData" el)))
|
((fd (host-new "FormData" el)))
|
||||||
(dict "url" url "body" fd "content-type" nil))
|
(dict "url" url "body" fd "content-type" nil))
|
||||||
|
;; SX-native wire: serialise the form fields to a text/sx body
|
||||||
|
;; (the host reads it via host/sx-body / host/field). A hydrated
|
||||||
|
;; page posts SX, not urlencoded; the server still accepts
|
||||||
|
;; urlencoded for the no-engine fallback. See plans/
|
||||||
|
;; relations-as-posts.md ("SX all the way out").
|
||||||
(let
|
(let
|
||||||
((fd (host-new "FormData" el))
|
((payload
|
||||||
(params (host-new "URLSearchParams" fd)))
|
(reduce
|
||||||
|
(fn (acc f)
|
||||||
|
(let ((nm (dom-get-attr f "name")))
|
||||||
|
(if (and nm (not (= nm "")))
|
||||||
|
(assoc acc nm (or (host-get f "value") ""))
|
||||||
|
acc)))
|
||||||
|
(dict)
|
||||||
|
(dom-query-all el "input, textarea, select"))))
|
||||||
(dict
|
(dict
|
||||||
"url"
|
"url"
|
||||||
url
|
url
|
||||||
"body"
|
"body"
|
||||||
(host-call params "toString")
|
(serialize payload)
|
||||||
"content-type"
|
"content-type"
|
||||||
"application/x-www-form-urlencoded"))))
|
"text/sx; charset=utf-8"))))
|
||||||
(dict "url" url "body" nil "content-type" nil))))))
|
(dict "url" url "body" nil "content-type" nil))))))
|
||||||
(define abort-previous-target (fn (el) nil))
|
(define abort-previous-target (fn (el) nil))
|
||||||
(define abort-previous (fn (el) nil))
|
(define abort-previous (fn (el) nil))
|
||||||
|
|||||||
Reference in New Issue
Block a user