WIP trmnl BYOS
1package fetch
2
3import (
4 "encoding/json"
5 "fmt"
6 "io"
7 "net/http"
8 "strings"
9
10 "github.com/dop251/goja"
11)
12
13type response struct {
14 status int
15 statusText string
16 url string
17 body []byte
18}
19
20func (r *response) toObject(runtime *goja.Runtime) *goja.Object {
21 obj := runtime.NewObject()
22
23 obj.Set("ok", runtime.ToValue(r.status > 199 && r.status < 300))
24 obj.Set("status", runtime.ToValue(r.status))
25 obj.Set("statusText", runtime.ToValue(r.statusText))
26 obj.Set("url", runtime.ToValue(r.url))
27 obj.Set("text", runtime.ToValue(r.getText(runtime)))
28 obj.Set("json", runtime.ToValue(r.getJSON(runtime)))
29
30 return obj
31}
32
33func (r *response) getText(runtime *goja.Runtime) func(goja.FunctionCall) goja.Value {
34 return func(goja.FunctionCall) goja.Value {
35 return runtime.ToValue(string(r.body))
36 }
37}
38
39func (r *response) getJSON(runtime *goja.Runtime) func(goja.FunctionCall) goja.Value {
40 return func(goja.FunctionCall) goja.Value {
41 data := map[string]any{}
42
43 if err := json.Unmarshal(r.body, &data); err != nil {
44 // TODO: throw exception?
45 return runtime.NewGoError(err)
46 }
47
48 return runtime.ToValue(data)
49 }
50}
51
52type fetchOptions struct {
53 Headers map[string]string
54}
55
56func doFetch(runtime *goja.Runtime) func(goja.FunctionCall) goja.Value {
57 return func(fc goja.FunctionCall) goja.Value {
58 url := fc.Argument(0).String()
59
60 opts := map[string]any{}
61
62 params := fc.Argument(1)
63 if !goja.IsUndefined(params) {
64 if err := runtime.ExportTo(params, &opts); err != nil {
65 return runtime.NewGoError(err)
66 }
67 }
68
69 headers := map[string][]string{}
70 if hdrs, ok := opts["headers"]; ok {
71 if h, ok := hdrs.(map[string]any); ok {
72 for k, v := range h {
73 switch vt := v.(type) {
74 case string:
75 headers[k] = []string{vt}
76 case int:
77 headers[k] = []string{fmt.Sprintf("%d", vt)}
78 }
79 }
80 }
81 }
82
83 res := &response{}
84
85 req, err := http.NewRequest("GET", url, nil)
86 if err != nil {
87 return runtime.NewGoError(err)
88 }
89
90 req.Header = headers
91
92 client := &http.Client{}
93 resp, err := client.Do(req)
94 if err != nil {
95 return runtime.NewGoError(err)
96 }
97 defer resp.Body.Close()
98
99 res.status = resp.StatusCode
100 res.statusText = strings.TrimPrefix(resp.Status, fmt.Sprintf("%d ", resp.StatusCode))
101 res.url = resp.Request.URL.String()
102
103 res.body, _ = io.ReadAll(resp.Body)
104
105 return res.toObject(runtime)
106 }
107}
108
109func Enable(runtime *goja.Runtime) {
110 runtime.Set("fetch", doFetch(runtime))
111}