Documentation
¶
Overview ¶
Package live runs stateful components for a connected browser: a Session keeps the component instances of one page, calls their event handlers and Update methods, and answers each with the HTML of the components whose output changed. It knows no transport; a WebSocket server drives it with the messages of protocol.go.
Index ¶
- Constants
- Variables
- func AllowPush(typ string, methods ...string)
- func Marshal(m ServerMessage) []byte
- func PublicMessage(err error) (string, bool)
- type ClientMessage
- type JSPush
- type Patch
- type PublicError
- type Route
- type Router
- type ServerMessage
- type Session
- func (s *Session) Adopt(ctx context.Context, render func(ctx context.Context) ui.Node) error
- func (s *Session) Close()
- func (s *Session) Component(target string) ui.Stateful
- func (s *Session) Deferred() []*ui.DeferredLoad
- func (s *Session) ErrorText(err error) string
- func (s *Session) Event(ev ui.Event) (Patch, error)
- func (s *Session) HTML() string
- func (s *Session) Handle(m ClientMessage) ServerMessage
- func (s *Session) HandleJSON(data []byte) []byte
- func (s *Session) Handler(target string) any
- func (s *Session) Info(topic string, msg any) (Patch, error)
- func (s *Session) JS() []JSPush
- func (s *Session) Loaded(d *ui.DeferredLoad) (Patch, error)
- func (s *Session) Navigate(rt Route, patch bool) (Patch, error)
- func (s *Session) Render() (string, error)
- func (s *Session) RenderTrees() ([]TreeUpdate, error)
- func (s *Session) SetErrorText(text func(err error) string)
- func (s *Session) SetRouter(r Router)
- func (s *Session) Topics() []string
- type TreeUpdate
- type Update
Constants ¶
const ( Join = "join" // client: render the page (the first message) EventMsg = "event" // client: an element's event, to the handler Target names Render = "render" // server: the page's whole HTML, for the container PatchMsg = "patch" // server: the components that changed, after an event or a broadcast Error = "error" // server: the message failed; the session goes on Redirect = "redirect" // server: load URL, a full page load )
The wire protocol: JSON messages over one connection per page (a WebSocket, typically), client → server ClientMessage, server → client ServerMessage.
→ {"type":"join"}
← {"type":"render","html":"<main>…</main>"}
→ {"type":"event","ref":1,"target":"c2:0","event":"click"}
← {"type":"patch","ref":1,"updates":[{"id":"c2","html":"<div data-vk-id=\"c2\">…</div>"}]}
→ {"type":"event","ref":2,"target":"c3:1","event":"submit","form":{"title":["Hi"]}}
← {"type":"patch","ref":2,"updates":[…],"error":"title is taken"}
← {"type":"patch","updates":[…]} (a broadcast: Info)
← {"type":"error","ref":3,"error":"live: no handler \"c9:0\""}
→ {"type":"event","ref":4,"target":"c2:Inc","event":"push","value":"5"}
A target names a handler the instance rendered by its number, or a method of its component by name (what the component's scripts push), when AllowPush allows it; any other target is an error.
Every event gets exactly one reply carrying its ref — a patch, empty when nothing changed ({"type":"patch","ref":4}), or an error — which is what settles an optimistic push in the browser.
render and patch messages may carry the JS commands components pushed, {"js":[{"id":"c2","ops":[…]}]}, run after the message is applied.
A client joining with {"type":"join","v":2} gets version 2: render and patch carry each instance's render tree, or its change, as Trees (components.md, "Protocol v2"):
→ {"type":"join","v":2}
← {"type":"render","v":2,"trees":[{"id":"c0","full":true,"tree":{"t":0,"s":["<main>","","</main>"],"d":[{"id":"c1"}]}},…]}
→ {"type":"event","ref":1,"target":"c1:0","event":"click"}
← {"type":"patch","ref":1,"trees":[{"id":"c1","tree":{"u":{"2":"1"}}}]}
A page moves to another URL over its connection (both versions):
→ {"type":"navigate","ref":5,"url":"/books?page=2","patch":true}
← {"type":"patch","ref":5,"url":"/books?page=2","trees":[…]}
→ {"type":"navigate","ref":6,"url":"/books/7"}
← {"type":"patch","ref":6,"url":"/books/7","trees":[…]}
→ {"type":"navigate","ref":7,"url":"/report.pdf"}
← {"type":"redirect","ref":7,"url":"/report.pdf"} (not a page: load it)
const RootID = "c0"
RootID is the id of the page's own render, which owns the handlers of elements outside any stateful component.
Variables ¶
var Verify = verifyDefault()
Verify makes new sessions render the components Assign tracking would skip, and fail the event (or message) when the render differs from the one kept: a state field changed without Assign.Set, or Render read something else. It is on in tests and with VUKA_LIVE_VERIFY=1, off with VUKA_LIVE_VERIFY=0. Verifying sessions also check that statics shared by a fingerprint are the same.
Functions ¶
func AllowPush ¶ added in v0.7.0
AllowPush lets a component's scripts push its methods by name: typ is the component's struct type as reflect spells it ("main.Counter", "example.com/app/views.Chart"), methods the names its scripts push, replacing any allowed before (none: no pushes by name). The vuka command lists them (each ctx.push("Name") of the scripts) and web registers them from its manifest. A component nothing allows accepts no pushes by name; the handlers its page renders work regardless. Render, Mount, an Update and methods promoted from what it embeds are never pushed. "_set" allows setting its bind: fields (ui.SetBound), as a page script's live.<field>.set(v) does.
func Marshal ¶
func Marshal(m ServerMessage) []byte
Marshal is a message's JSON, markup written as markup (not \u003c…).
func PublicMessage ¶ added in v0.10.0
PublicMessage is the message of the first PublicError in err's chain, and whether there is one.
Types ¶
type ClientMessage ¶
type ClientMessage struct {
Type string `json:"type"`
Ref int `json:"ref,omitempty"`
V int `json:"v,omitempty"` // join: the protocol version the client speaks (1 when 0)
// navigate: the URL to move to (a path, with its query), and whether
// it is a patch: the same page with other parameters.
URL string `json:"url,omitempty"`
Patch bool `json:"patch,omitempty"`
ui.Event
}
ClientMessage is a message from the browser. Ref, any number the client picks, comes back on the reply.
type JSPush ¶ added in v0.2.0
type JSPush struct {
ID string `json:"id"`
Ops json.RawMessage `json:"ops"`
}
JSPush is JS commands a component pushed (ui.Live.PushJS), for the browser to run once the message carrying it is applied: Ops, the commands' JSON, run with the element whose data-vk-id is ID as This.
type Patch ¶
type Patch struct {
Updates []Update `json:"updates,omitempty"`
Trees []TreeUpdate `json:"trees,omitempty"`
JS []JSPush `json:"js,omitempty"`
Error string `json:"error,omitempty"`
}
Patch is what an event or message changed: the HTML of each component whose output changed, outermost first (a changed component's update includes the components inside it), and the error a handler returned.
type PublicError ¶ added in v0.10.0
PublicError is an error whose message is meant for the person using the page: a transport that hides errors' text from the browser (see Session.SetErrorText) still shows PublicMessage. A form field that doesn't parse is one; an error a web framework answers with a status and a message can be one too.
type Route ¶ added in v0.4.0
type Route struct {
Ctx context.Context
Render func(ctx context.Context) ui.Node
Redirect string
Updates map[ui.Stateful]any
}
Route is the page a URL renders, what a Router gives Navigate: Render, the page's render function, with Ctx (when set) the context of what runs from then on, or, with Render nil, Redirect, a URL for the browser to load (the URL itself when empty). Updates holds messages for a patch: an instance kept for a tag whose props are a key takes the value in its Update method before it renders.
type ServerMessage ¶
type ServerMessage struct {
Type string `json:"type"`
Ref int `json:"ref,omitempty"`
V int `json:"v,omitempty"` // render: 2 for a v2 render
HTML string `json:"html,omitempty"`
Updates []Update `json:"updates,omitempty"`
Trees []TreeUpdate `json:"trees,omitempty"`
JS []JSPush `json:"js,omitempty"`
Error string `json:"error,omitempty"`
URL string `json:"url,omitempty"` // redirect: the URL to load; a navigation's patch: the page's new URL
}
ServerMessage is a message to the browser.
func PatchMessage ¶
func PatchMessage(ref int, p Patch) ServerMessage
PatchMessage is the message carrying p, the reply to Ref (0 for a broadcast).
type Session ¶
type Session struct {
// contains filtered or unexported fields
}
Session is one connected page: its render function and the instances of the stateful components it renders. Its methods are safe to call from several goroutines; each runs alone.
func NewSession ¶
NewSession is a session rendering the page render returns. ctx is the context of Mount, handlers and Update methods, and of every render.
func (*Session) Adopt ¶ added in v0.11.0
Adopt hands the session to a new connection: ctx is the context of its handlers, Update methods and renders from now on, and render (when not nil) the page's render function. The instances stay mounted with their state, so a session kept from a page's HTTP render can serve its live connection without mounting again: the next Render or RenderTrees renders the kept instances, and commands they pushed while mounting are still in JS. The old context must not be needed again — it is usually a finished request's.
func (*Session) Close ¶
func (s *Session) Close()
Close unmounts every instance. The session renders nothing after it.
func (*Session) Component ¶ added in v0.8.0
Component is the component an event's target belongs to ("c1:0", "c1:Inc"): the instance's, for a transport to read what its type declares (a gate on the type, refusing every event of it) before the handler runs; nil when the target names no instance, or one that is no component (the page's root).
func (*Session) Deferred ¶ added in v0.30.0
func (s *Session) Deferred() []*ui.DeferredLoad
Deferred starts the deferred loads of the pages backed by their islands the session holds (ui.StartDeferred), each once: the transport runs each (Run, with a context ending when the page does) once the page has joined, and hands it to Loaded.
func (*Session) ErrorText ¶ added in v0.10.0
ErrorText is err's text for the browser, as SetErrorText says.
func (*Session) Event ¶
Event calls the handler ev.Target names with the event's payload, renders the page again, and returns the components whose output changed. A handler's error is the patch's Error; the returned error is the session's: an unknown handler, or a render that failed.
func (*Session) Handle ¶
func (s *Session) Handle(m ClientMessage) ServerMessage
Handle answers a client message.
func (*Session) HandleJSON ¶
HandleJSON answers a client message's JSON with the reply's.
func (*Session) Handler ¶ added in v0.6.1
Handler is the function an event's target names ("c1:0"): a method value such as c.Inc, for a transport to say which handler an event ran (a development toolbar's "Counter.Inc"); nil when it names none.
func (*Session) Info ¶
Info delivers msg to the Update method fitting its type of every instance subscribed to topic (of every instance, for ""), renders the page again if any took it, and returns what changed. An Update's error is the patch's Error.
func (*Session) JS ¶ added in v0.2.0
JS is the commands components pushed while the last Render or RenderTrees mounted them (from a connected Mount), for the join's reply; it returns them once.
func (*Session) Loaded ¶ added in v0.30.0
func (s *Session) Loaded(d *ui.DeferredLoad) (Patch, error)
Loaded gives its page what the deferred load d loaded (or its error, as the session's error text says, for the component to read), renders the page again, and returns what changed.
func (*Session) Navigate ¶ added in v0.4.0
Navigate makes rt's page the session's, renders it, and returns what changed, as an event's patch. As a patch (patch true), the instances the new page renders where the old one rendered them stay mounted, their props set from its tags, and those rt.Updates names take their message first. Otherwise every instance is unmounted and the new page's mount afresh. The page itself (c0) is the same instance either way, so a client gets only what changed of its markup — on a v2 connection, against the statics it already holds.
The router decides what may be kept, not the client asking for a patch: a patch with no rt.Updates is a navigation, every instance mounting afresh, and an instance whose tag rt.Updates names is kept only when an Update method of its takes the message — else it mounts afresh, so its Mount sees the new URL. A component that checks in Mount whether the visitor may see what its props name must check again in that Update.
An error — a Mount's or an Update's, or the render's — fails the navigation: Navigate returns it, every instance is unmounted, and the page has no handlers until it navigates or joins again (a browser loads the URL itself, getting the route's own answer).
func (*Session) Render ¶
Render renders the whole page, mounting the components it meets for the first time, and returns its HTML: what the page's container holds.
func (*Session) RenderTrees ¶
func (s *Session) RenderTrees() ([]TreeUpdate, error)
RenderTrees makes the session a v2 one and renders the whole page as every instance's render tree, the page's first: what a v2 client joins with. From then on patches carry Trees instead of Updates. It starts the connection afresh: every statics id and kept string is forgotten.
func (*Session) SetErrorText ¶ added in v0.10.0
SetErrorText sets how the session turns an error into the text a message to the browser carries: what a handler, Mount or Update returned, what it panicked with, and a render's or the session's own failure. Without it the text is err.Error(), which may say more than the browser should see (a query, a path, a panic's value): a transport serving pages to the public sets it, showing PublicMessage (or a generic message) and logging the rest.
type TreeUpdate ¶
type TreeUpdate struct {
ID string `json:"id"`
Full bool `json:"full,omitempty"`
Tree json.RawMessage `json:"tree,omitempty"`
HTML string `json:"html,omitempty"`
}
TreeUpdate is an instance's change on a v2 connection (see RenderTrees): its whole tree (Full), the change to the tree the client holds, or its whole HTML when that is smaller. Tree is JSON, encoded as components.md ("Protocol v2") describes.
Source Files
¶
Directories
¶
| Path | Synopsis |
|---|---|
|
internal
|
|
|
demo
Package demo is stateful components compiled by the Vuka compiler, for the live package's property tests and benchmarks.
|
Package demo is stateful components compiled by the Vuka compiler, for the live package's property tests and benchmarks. |
|
templdemo
templ: version: v0.3.1020
|
templ: version: v0.3.1020 |
|
Package livetest is a reference client of the live protocol, in Go: it keeps what a browser runtime keeps — the page's HTML for v1, each instance's render tree for v2 — and applies the server's messages the way a runtime must, so tests can check that what a client ends up with is what the server rendered.
|
Package livetest is a reference client of the live protocol, in Go: it keeps what a browser runtime keeps — the page's HTML for v1, each instance's render tree for v2 — and applies the server's messages the way a runtime must, so tests can check that what a client ends up with is what the server rendered. |