package fetch import ( "encoding/json" "fmt" "io" "net/http" "strings" "github.com/dop251/goja" ) type response struct { status int statusText string url string body []byte } func (r *response) toObject(runtime *goja.Runtime) *goja.Object { obj := runtime.NewObject() obj.Set("ok", runtime.ToValue(r.status > 199 && r.status < 300)) obj.Set("status", runtime.ToValue(r.status)) obj.Set("statusText", runtime.ToValue(r.statusText)) obj.Set("url", runtime.ToValue(r.url)) obj.Set("text", runtime.ToValue(r.getText(runtime))) obj.Set("json", runtime.ToValue(r.getJSON(runtime))) return obj } func (r *response) getText(runtime *goja.Runtime) func(goja.FunctionCall) goja.Value { return func(goja.FunctionCall) goja.Value { return runtime.ToValue(string(r.body)) } } func (r *response) getJSON(runtime *goja.Runtime) func(goja.FunctionCall) goja.Value { return func(goja.FunctionCall) goja.Value { data := map[string]any{} if err := json.Unmarshal(r.body, &data); err != nil { // TODO: throw exception? return runtime.NewGoError(err) } return runtime.ToValue(data) } } type fetchOptions struct { Headers map[string]string } func doFetch(runtime *goja.Runtime) func(goja.FunctionCall) goja.Value { return func(fc goja.FunctionCall) goja.Value { url := fc.Argument(0).String() opts := map[string]any{} params := fc.Argument(1) if !goja.IsUndefined(params) { if err := runtime.ExportTo(params, &opts); err != nil { return runtime.NewGoError(err) } } headers := map[string][]string{} if hdrs, ok := opts["headers"]; ok { if h, ok := hdrs.(map[string]any); ok { for k, v := range h { switch vt := v.(type) { case string: headers[k] = []string{vt} case int: headers[k] = []string{fmt.Sprintf("%d", vt)} } } } } res := &response{} req, err := http.NewRequest("GET", url, nil) if err != nil { return runtime.NewGoError(err) } req.Header = headers client := &http.Client{} resp, err := client.Do(req) if err != nil { return runtime.NewGoError(err) } defer resp.Body.Close() res.status = resp.StatusCode res.statusText = strings.TrimPrefix(resp.Status, fmt.Sprintf("%d ", resp.StatusCode)) res.url = resp.Request.URL.String() res.body, _ = io.ReadAll(resp.Body) return res.toObject(runtime) } } func Enable(runtime *goja.Runtime) { runtime.Set("fetch", doFetch(runtime)) }