(:~ : WebSocket LSP : @author Andy Bunce :) module namespace lsp-ws = 'lsp-ws'; import module namespace rpc = 'rpc' at 'jsonrpc.xqm'; declare %ws:error('/lsp', '{$error}') function lsp-ws:error($error) { trace($error,"ERR ") }; (:~ Creates a WebSocket connection. :) declare %ws:connect('/lsp') function lsp-ws:connect() as empty-sequence() { let $id:=(ws:id(), rpc:admin-log("CoNNECT","🌹"), rpc:admin-log(request:header-map(),"🌹") ) let $props:=("host","http-version","is-secure","origin","protocol-version","query-string","request-uri","sub-protocols") return ( ws:set($id, "id", $id), ws:set($id, "connectTime", current-dateTime()), ws:set($id, "initialized", false()) ) }; (:~ : Processes a WebSocket message. : @param $message message :) declare %ws:message('/lsp', '{$message}') function lsp-ws:message( $message as xs:string ) as empty-sequence() { let $json := lsp-ws:parse($message) return if(exists($json)) then rpc:reply($json) else message($message,"bad RPC: ") }; (:~ : Closes a WebSocket connection. :) declare %ws:close('/lsp') function lsp-ws:close() as empty-sequence() { rpc:admin-log("CLOSE","🥀") }; (:~ return map if $msg is jsonrpc else empty :) declare function lsp-ws:parse($msg as xs:string) as map(*)? { try { let $jtext:=if(starts-with($msg,"{")) then $msg else lsp-ws:decode($msg) let $json:=parse-json($jtext) return if($json?jsonrpc eq "2.0" and exists($json?method)) then $json else () } catch *{ () } }; (:~ decode "Q29udGVudC1MZW5ndGg6IDU4NDUNCg0K" -> "Content-Length: 5845" :) declare function lsp-ws:decode($basex64 as xs:string) as xs:string{ xs:base64Binary($basex64) => bin:to-octets() => codepoints-to-string() };