//go:build js && wasm package wasmruntime import ( "syscall/js" "kjol/wasmruntime/vdom" ) // State preservation across an in-place hot swap (see the dev server's // livereload script). A fresh wasm instance has fresh Go memory, so signal // values would reset on every rebuild. To keep them, the outgoing instance // snapshots its signals (by creation order, via a vdom.Collector) into // sessionStorage on dispose, and the incoming instance restores them on boot. // // It's best-effort: if an edit adds/removes/reorders signals, the by-order match // shifts and unmatched signals fall back to their initial value — the same // hook-order caveat as server components. const stateKey = "__gowasm_hmr_state" var stateCollector *vdom.Collector // PreserveState registers the collector whose signals are snapshotted to // sessionStorage when this instance is disposed for a hot swap. func PreserveState(c *vdom.Collector) { stateCollector = c } // RestoreState returns the signal snapshot saved by a prior instance before a // hot swap, or nil if there is none. It is one-shot: the entry is cleared so a // manual page refresh starts from initial state. func RestoreState() [][]byte { ss := js.Global().Get("sessionStorage") if !ss.Truthy() { return nil } raw := ss.Call("getItem", stateKey) if !raw.Truthy() { return nil } ss.Call("removeItem", stateKey) arr := js.Global().Get("JSON").Call("parse", raw) if arr.Type() != js.TypeObject { return nil } out := make([][]byte, arr.Length()) for i := range out { out[i] = []byte(arr.Index(i).String()) } return out } // saveState writes the preserved collector's current signal values to // sessionStorage. Called from the dispose hook before the DOM is torn down. func saveState() { if stateCollector == nil { return } ss := js.Global().Get("sessionStorage") if !ss.Truthy() { return } arr := js.Global().Get("Array").New() for _, b := range stateCollector.Snapshot() { arr.Call("push", string(b)) } ss.Call("setItem", stateKey, js.Global().Get("JSON").Call("stringify", arr)) }