refactor folder structure
This commit is contained in:
107
go/wasmruntime/rsc/server.go
Normal file
107
go/wasmruntime/rsc/server.go
Normal file
@@ -0,0 +1,107 @@
|
||||
//go:build !(js && wasm)
|
||||
|
||||
package rsc
|
||||
|
||||
import (
|
||||
"encoding/gob"
|
||||
"net/http"
|
||||
"sort"
|
||||
"sync"
|
||||
|
||||
"kjol/wasmruntime/vdom"
|
||||
)
|
||||
|
||||
// registry maps a server-component name to its factory (created by generated
|
||||
// code — one Register per //gowasm:server component). The factory builds the
|
||||
// component's signals + render closure; here it's re-run per request (stateless).
|
||||
var registry = map[string]func() func() *vdom.VNode{}
|
||||
|
||||
func Register(name string, factory func() func() *vdom.VNode) { registry[name] = factory }
|
||||
|
||||
// renderMu serializes server renders because the vdom signal Collector is a
|
||||
// process-global (see vdom.BeginCollect). Fine for this scale.
|
||||
var renderMu sync.Mutex
|
||||
|
||||
// Handler is the single /rsc endpoint. It restores the component's signals from
|
||||
// the request, applies the event (if any), re-renders, and returns the new
|
||||
// signal snapshot + tree.
|
||||
func Handler(w http.ResponseWriter, r *http.Request) {
|
||||
var req Request
|
||||
if gob.NewDecoder(r.Body).Decode(&req) != nil {
|
||||
http.Error(w, "bad request", http.StatusBadRequest)
|
||||
return
|
||||
}
|
||||
factory := registry[req.Name]
|
||||
if factory == nil {
|
||||
http.NotFound(w, r)
|
||||
return
|
||||
}
|
||||
|
||||
renderMu.Lock()
|
||||
c := vdom.BeginCollect(req.State) // restore prior signal values (nil on mount)
|
||||
render := factory() // signals created + restored here
|
||||
vdom.EndCollect()
|
||||
|
||||
if req.Kind == "event" {
|
||||
// Reproduce the tree the client currently shows (same state) to find the
|
||||
// handler by node id, then invoke it (mutating signals).
|
||||
_, handlers := renderIDs(render())
|
||||
if hm := handlers[req.NodeID]; hm != nil {
|
||||
if h := hm[req.Event]; h != nil {
|
||||
h(serverEvent{value: req.Value})
|
||||
}
|
||||
}
|
||||
}
|
||||
tree, _ := renderIDs(render()) // render the result (reflects the mutation)
|
||||
resp := Response{State: c.Snapshot(), Tree: tree}
|
||||
renderMu.Unlock()
|
||||
|
||||
w.Header().Set("Content-Type", "application/octet-stream")
|
||||
_ = gob.NewEncoder(w).Encode(resp)
|
||||
}
|
||||
|
||||
// renderIDs serializes a VNode tree to SNode, assigning a stable (traversal
|
||||
// order) id to each node with handlers and building the handler table.
|
||||
func renderIDs(root *vdom.VNode) (*SNode, map[int]map[string]func(vdom.Event)) {
|
||||
handlers := map[int]map[string]func(vdom.Event){}
|
||||
id := 0
|
||||
var walk func(*vdom.VNode) *SNode
|
||||
walk = func(v *vdom.VNode) *SNode {
|
||||
sn := &SNode{Tag: v.Tag, Text: v.Text, HTML: v.HTML, Attrs: v.Attrs}
|
||||
if len(v.Events) > 0 {
|
||||
id++
|
||||
sn.ID = id
|
||||
hm := map[string]func(vdom.Event){}
|
||||
for ev, h := range v.Events {
|
||||
sn.Events = append(sn.Events, ev)
|
||||
hm[ev] = h
|
||||
}
|
||||
sort.Strings(sn.Events)
|
||||
handlers[id] = hm
|
||||
}
|
||||
if v.HTML == "" {
|
||||
for _, k := range v.Children {
|
||||
sn.Kids = append(sn.Kids, walk(k))
|
||||
}
|
||||
}
|
||||
return sn
|
||||
}
|
||||
return walk(root), handlers
|
||||
}
|
||||
|
||||
// serverEvent is the vdom.Event a server component sees when the client replays a
|
||||
// handler invocation. Only the target's value survives the round trip (it is all
|
||||
// the client sends); everything else — key, pointer coordinates, the DOM target,
|
||||
// the DataTransfer — is meaningless on the server and reads as its zero value.
|
||||
type serverEvent struct{ value string }
|
||||
|
||||
func (e serverEvent) PreventDefault() {}
|
||||
func (e serverEvent) StopPropagation() {}
|
||||
func (e serverEvent) Value() string { return e.value }
|
||||
func (e serverEvent) Checked() bool { return false }
|
||||
func (e serverEvent) Key() string { return "" }
|
||||
func (e serverEvent) ClientX() int { return 0 }
|
||||
func (e serverEvent) ClientY() int { return 0 }
|
||||
func (e serverEvent) Target() any { return nil }
|
||||
func (e serverEvent) SetData(_, _ string) {}
|
||||
func (e serverEvent) GetData(string) string { return "" }
|
||||
Reference in New Issue
Block a user