HS: socket feature (E36) — WebSocket wrapper + RPC proxy (+16 tests)
Some checks failed
Test, Build, and Deploy / test-build-deploy (push) Has been cancelled

Parser: socket feature (name, url, with timeout, on message, json/raw).
Runtime: hs-socket-register!, hs-socket-normalise-url, hs-socket-bind-name!,
  hs-socket-reconnect!, hs-socket-rpc!, hs-socket-resolve-rpc! — full
  WebSocket lifecycle with reconnect, pending-map RPC, and timeout.
Compiler: compile-socket-feat stub (feature is self-registering at activation).
Test harness: dispatch-object pattern for RPC proxy — OCaml WASM kernel cannot
  return values created inside a JS Proxy get trap; plain function with
  _hsRpcDispatch method + host-get intercept avoids the limitation.
Test suite: 16 new tests (hs-upstream-socket) covering URL normalisation,
  socket registration, on-message, JSON/raw, RPC calls, timeout, reconnect,
  noTimeout modifier, reply-with-throw. 16/16 pass.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
This commit is contained in:
2026-05-06 11:44:13 +00:00
parent 0f63216adc
commit 623529d3be
7 changed files with 886 additions and 42 deletions

View File

@@ -3178,6 +3178,35 @@
(match-kw "end")
(list (quote bind-feat) lhs rhs)))
(true (do (match-kw "end") (list (quote bind-feat) lhs nil)))))))
(define
parse-socket-feat
(fn
()
(let
((first-seg (tp-val)))
(do
(adv!)
(define
collect-dots!
(fn
(acc)
(if
(= (tp-type) "class")
(let
((seg (tp-val)))
(do (adv!) (collect-dots! (append acc (list seg)))))
acc)))
(let
((name-path (collect-dots! (list first-seg))))
(let
((url (parse-arith (parse-poss (parse-atom)))))
(let
((timeout (if (match-kw "with") (do (when (and (or (= (tp-type) "ident") (= (tp-type) "keyword")) (= (tp-val) "timeout")) (adv!)) (parse-arith (parse-poss (parse-atom)))) nil)))
(let
((on-message (if (and (= (tp-type) "keyword") (= (tp-val) "on")) (do (adv!) (when (and (or (= (tp-type) "ident") (= (tp-type) "keyword")) (= (tp-val) "message")) (do (adv!) (let ((json? (if (match-kw "as") (do (when (and (or (= (tp-type) "ident") (= (tp-type) "keyword")) (= (tp-val) "JSON")) (adv!)) true) false))) (let ((body (parse-cmd-list))) (list (quote on-message) json? body)))))) nil)))
(do
(match-kw "end")
(list (quote socket) name-path url timeout on-message))))))))))
(define
parse-feat
(fn
@@ -3218,6 +3247,7 @@
(error
"worker plugin is not installed — see https://hyperscript.org/features/worker"))
((= val "bind") (do (adv!) (parse-bind-feat)))
((= val "socket") (do (adv!) (parse-socket-feat)))
(true
(if
(= (tp-type) "keyword")
@@ -3263,9 +3293,12 @@
(define hs-compile (fn (src) (hs-parse (hs-tokenize src) src)))
(define hs-parse-ast
(fn (src)
(define
hs-parse-ast
(fn
(src)
(do
(set! hs-span-mode true)
(let ((result (hs-parse (hs-tokenize src) src)))
(let
((result (hs-parse (hs-tokenize src) src)))
(do (set! hs-span-mode false) result)))))