gob fetcher for wasm example

This commit is contained in:
2026-07-13 02:24:06 -04:00
parent b02df48a66
commit 93e36c9abc
13 changed files with 339 additions and 6 deletions

View File

@@ -5,10 +5,22 @@ package wasmruntime
import (
"errors"
"fmt"
"strings"
"sync"
"syscall/js"
)
// absURL resolves a relative path against the current origin (a browser resolves
// relative fetch URLs itself, but building the absolute URL keeps behavior
// uniform — and lets non-browser hosts, e.g. tests, fetch too).
func absURL(url string) string {
if strings.HasPrefix(url, "http://") || strings.HasPrefix(url, "https://") {
return url
}
loc := js.Global().Get("location")
return loc.Get("protocol").String() + "//" + loc.Get("host").String() + url
}
// Fetch performs an HTTP GET via the browser Fetch API and returns the response
// body as a string. It bridges a JS Promise into Go: it registers then/catch
// callbacks and blocks the calling goroutine on a channel until the request
@@ -67,7 +79,7 @@ func Fetch(url string) (string, error) {
return nil
})
js.Global().Call("fetch", url).Call("then", onResp).Call("catch", onErr)
js.Global().Call("fetch", absURL(url)).Call("then", onResp).Call("catch", onErr)
<-done
onResp.Release()
@@ -75,3 +87,63 @@ func Fetch(url string) (string, error) {
onErr.Release()
return body, ferr
}
// FetchBytes is like Fetch but returns the raw response body as bytes (via
// resp.arrayBuffer()), for binary payloads such as gob (Fetch's resp.text()
// would corrupt non-UTF-8 data). It blocks the calling goroutine the same way —
// call it from a goroutine and write the result into signals when it returns.
func FetchBytes(url string) ([]byte, error) {
var (
once sync.Once
done = make(chan struct{})
body []byte
ferr error
onResp, onBuf, onErr js.Func
)
finish := func() { once.Do(func() { close(done) }) }
onErr = js.FuncOf(func(this js.Value, args []js.Value) any {
msg := "fetch error"
if len(args) > 0 && args[0].Truthy() {
e := args[0]
if m := e.Get("message"); m.Truthy() {
msg = m.String()
} else {
msg = e.Call("toString").String()
}
}
ferr = errors.New(msg)
finish()
return nil
})
onBuf = js.FuncOf(func(this js.Value, args []js.Value) any {
if len(args) > 0 {
arr := js.Global().Get("Uint8Array").New(args[0])
b := make([]byte, arr.Get("length").Int())
js.CopyBytesToGo(b, arr)
body = b
}
finish()
return nil
})
onResp = js.FuncOf(func(this js.Value, args []js.Value) any {
resp := args[0]
if !resp.Get("ok").Bool() {
ferr = fmt.Errorf("HTTP %d", resp.Get("status").Int())
finish()
return nil
}
resp.Call("arrayBuffer").Call("then", onBuf).Call("catch", onErr)
return nil
})
js.Global().Call("fetch", absURL(url)).Call("then", onResp).Call("catch", onErr)
<-done
onResp.Release()
onBuf.Release()
onErr.Release()
return body, ferr
}