From 973d5eb077b375d85c3f36c815d434b9f90a9e85 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Mon, 10 Jun 2024 07:54:14 +0000 Subject: [PATCH] Upgrade experimental module (deps): Bump github.com/grafana/xk6-websockets Bumps [github.com/grafana/xk6-websockets](https://github.com/grafana/xk6-websockets) from 0.4.0 to 0.5.1. - [Commits](https://github.com/grafana/xk6-websockets/compare/v0.4.0...v0.5.1) --- updated-dependencies: - dependency-name: github.com/grafana/xk6-websockets dependency-type: direct:production update-type: version-update:semver-minor ... Signed-off-by: dependabot[bot] --- go.mod | 2 +- go.sum | 4 +- .../xk6-websockets/websockets/helpers.go | 4 +- .../xk6-websockets/websockets/listeners.go | 24 ++-- .../xk6-websockets/websockets/params.go | 11 +- .../xk6-websockets/websockets/websockets.go | 115 ++++++++++++------ vendor/modules.txt | 4 +- 7 files changed, 100 insertions(+), 64 deletions(-) diff --git a/go.mod b/go.mod index 14a2d1df21f..f505c8601e9 100644 --- a/go.mod +++ b/go.mod @@ -20,7 +20,7 @@ require ( github.com/grafana/xk6-output-prometheus-remote v0.3.1 github.com/grafana/xk6-redis v0.2.0 github.com/grafana/xk6-webcrypto v0.3.0 - github.com/grafana/xk6-websockets v0.4.0 + github.com/grafana/xk6-websockets v0.5.1 github.com/grpc-ecosystem/go-grpc-middleware v1.4.0 github.com/influxdata/influxdb1-client v0.0.0-20190402204710-8ff2fc3824fc github.com/jhump/protoreflect v1.15.6 diff --git a/go.sum b/go.sum index e197a3286dc..451ae66f479 100644 --- a/go.sum +++ b/go.sum @@ -95,8 +95,8 @@ github.com/grafana/xk6-redis v0.2.0 h1:iXmAKVlAxafZ/h8ptuXTFhGu63IFsyDI8QjUgWm66 github.com/grafana/xk6-redis v0.2.0/go.mod h1:B3PA9PAPJa2/WUfNJCdQwZrbb6D4e6UHIk8dssQbj7w= github.com/grafana/xk6-webcrypto v0.3.0 h1:piwiTrLTQDbuzC4CK0dVjbzQqNhIoGjWXrflf++W8aE= github.com/grafana/xk6-webcrypto v0.3.0/go.mod h1:Jk+UKbo+w/RCQH0hi+Hoy7Uj2HWd+dwEBjn1EDpwY0w= -github.com/grafana/xk6-websockets v0.4.0 h1:gK0ekd7nIO4tPDzWk4ljQGzMep5Usy3Y/iuXZM7xXHI= -github.com/grafana/xk6-websockets v0.4.0/go.mod h1:sykcMskoS8qA8/XWED5h7qg7xsJXqzdKYOM0+inAz9s= +github.com/grafana/xk6-websockets v0.5.1 h1:wymI6UWpwDorv3mEInytrQjC9cmXYxQFygBOCMY1q6k= +github.com/grafana/xk6-websockets v0.5.1/go.mod h1:yPadv8R00MPCnV+GGSlYV/vwVgxKRCiiJoIfWsNGoQg= github.com/grpc-ecosystem/go-grpc-middleware v1.4.0 h1:UH//fgunKIs4JdUbpDl1VZCDaL56wXCB/5+wF6uHfaI= github.com/grpc-ecosystem/go-grpc-middleware v1.4.0/go.mod h1:g5qyo/la0ALbONm6Vbp88Yd8NsDy6rZz+RcrMPxvld8= github.com/grpc-ecosystem/grpc-gateway/v2 v2.19.0 h1:Wqo399gCIufwto+VfwCSvsnfGpF/w5E9CNxSwbpD6No= diff --git a/vendor/github.com/grafana/xk6-websockets/websockets/helpers.go b/vendor/github.com/grafana/xk6-websockets/websockets/helpers.go index 0ea0dbabb71..282b45f754e 100644 --- a/vendor/github.com/grafana/xk6-websockets/websockets/helpers.go +++ b/vendor/github.com/grafana/xk6-websockets/websockets/helpers.go @@ -1,12 +1,12 @@ package websockets import ( - "github.com/dop251/goja" + "github.com/grafana/sobek" "go.k6.io/k6/js/common" ) // must is a small helper that will panic if err is not nil. -func must(rt *goja.Runtime, err error) { +func must(rt *sobek.Runtime, err error) { if err != nil { common.Throw(rt, err) } diff --git a/vendor/github.com/grafana/xk6-websockets/websockets/listeners.go b/vendor/github.com/grafana/xk6-websockets/websockets/listeners.go index d3fe03f93b4..0d6483a8622 100644 --- a/vendor/github.com/grafana/xk6-websockets/websockets/listeners.go +++ b/vendor/github.com/grafana/xk6-websockets/websockets/listeners.go @@ -3,7 +3,7 @@ package websockets import ( "fmt" - "github.com/dop251/goja" + "github.com/grafana/sobek" "github.com/grafana/xk6-websockets/websockets/events" ) @@ -34,10 +34,10 @@ func newEventListeners() *eventListeners { type eventListener struct { eventType string - // this return goja.value *and* error in order to return error on exception instead of panic + // this return sobek.value *and* error in order to return error on exception instead of panic // https://pkg.go.dev/github.com/dop251/goja#hdr-Functions - on func(goja.Value) (goja.Value, error) - list []func(goja.Value) (goja.Value, error) + on func(sobek.Value) (sobek.Value, error) + list []func(sobek.Value) (sobek.Value, error) } // newListener creates a new listener of a certain type @@ -48,27 +48,27 @@ func newListener(eventType string) *eventListener { } // add adds a listener to the listener list -func (l *eventListener) add(fn func(goja.Value) (goja.Value, error)) { +func (l *eventListener) add(fn func(sobek.Value) (sobek.Value, error)) { l.list = append(l.list, fn) } // setOn sets a listener for the on* properties, like onopen, onmessage, etc. -func (l *eventListener) setOn(fn func(goja.Value) (goja.Value, error)) { +func (l *eventListener) setOn(fn func(sobek.Value) (sobek.Value, error)) { l.on = fn } // getOn returns the on* property for a certain event type -func (l *eventListener) getOn() func(goja.Value) (goja.Value, error) { +func (l *eventListener) getOn() func(sobek.Value) (sobek.Value, error) { return l.on } // return all possible listeners for a certain event type -func (l *eventListener) all() []func(goja.Value) (goja.Value, error) { +func (l *eventListener) all() []func(sobek.Value) (sobek.Value, error) { if l.on == nil { return l.list } - return append([]func(goja.Value) (goja.Value, error){l.on}, l.list...) + return append([]func(sobek.Value) (sobek.Value, error){l.on}, l.list...) } // getTypes return event listener of a certain type @@ -92,7 +92,7 @@ func (l *eventListeners) getType(t string) *eventListener { } // add adds a listener to the listeners -func (l *eventListeners) add(t string, f func(goja.Value) (goja.Value, error)) error { +func (l *eventListeners) add(t string, f func(sobek.Value) (sobek.Value, error)) error { list := l.getType(t) if list == nil { @@ -105,11 +105,11 @@ func (l *eventListeners) add(t string, f func(goja.Value) (goja.Value, error)) e } // all returns all possible listeners for a certain event type or an empty array -func (l *eventListeners) all(t string) []func(goja.Value) (goja.Value, error) { +func (l *eventListeners) all(t string) []func(sobek.Value) (sobek.Value, error) { list := l.getType(t) if list == nil { - return []func(goja.Value) (goja.Value, error){} + return []func(sobek.Value) (sobek.Value, error){} } return list.all() diff --git a/vendor/github.com/grafana/xk6-websockets/websockets/params.go b/vendor/github.com/grafana/xk6-websockets/websockets/params.go index 6398d15dc0a..395f6886bda 100644 --- a/vendor/github.com/grafana/xk6-websockets/websockets/params.go +++ b/vendor/github.com/grafana/xk6-websockets/websockets/params.go @@ -6,7 +6,7 @@ import ( "net/http/cookiejar" "strings" - "github.com/dop251/goja" + "github.com/grafana/sobek" "go.k6.io/k6/js/common" httpModule "go.k6.io/k6/js/modules/k6/http" @@ -20,10 +20,11 @@ type wsParams struct { cookieJar *cookiejar.Jar tagsAndMeta *metrics.TagsAndMeta enableCompression bool + subprocotols []string } // buildParams builds WebSocket params and configure some of them -func buildParams(state *lib.State, rt *goja.Runtime, raw goja.Value) (*wsParams, error) { +func buildParams(state *lib.State, rt *sobek.Runtime, raw sobek.Value) (*wsParams, error) { tagsAndMeta := state.Tags.GetCurrentValues() parsed := &wsParams{ @@ -34,7 +35,7 @@ func buildParams(state *lib.State, rt *goja.Runtime, raw goja.Value) (*wsParams, parsed.headers.Set("User-Agent", state.Options.UserAgent.String) - if raw == nil || goja.IsUndefined(raw) { + if raw == nil || sobek.IsUndefined(raw) { return parsed, nil } @@ -43,7 +44,7 @@ func buildParams(state *lib.State, rt *goja.Runtime, raw goja.Value) (*wsParams, switch k { case "headers": headersV := params.Get(k) - if goja.IsUndefined(headersV) || goja.IsNull(headersV) { + if sobek.IsUndefined(headersV) || sobek.IsNull(headersV) { continue } headersObj := headersV.ToObject(rt) @@ -59,7 +60,7 @@ func buildParams(state *lib.State, rt *goja.Runtime, raw goja.Value) (*wsParams, } case "jar": jarV := params.Get(k) - if goja.IsUndefined(jarV) || goja.IsNull(jarV) { + if sobek.IsUndefined(jarV) || sobek.IsNull(jarV) { continue } if v, ok := jarV.Export().(*httpModule.CookieJar); ok { diff --git a/vendor/github.com/grafana/xk6-websockets/websockets/websockets.go b/vendor/github.com/grafana/xk6-websockets/websockets/websockets.go index 4651be0a6b2..008d40fe100 100644 --- a/vendor/github.com/grafana/xk6-websockets/websockets/websockets.go +++ b/vendor/github.com/grafana/xk6-websockets/websockets/websockets.go @@ -13,8 +13,8 @@ import ( "sync" "time" - "github.com/dop251/goja" "github.com/gorilla/websocket" + "github.com/grafana/sobek" "github.com/grafana/xk6-websockets/websockets/events" "github.com/mstoykov/k6-taskqueue-lib/taskqueue" @@ -70,7 +70,7 @@ type webSocket struct { tagsAndMeta *metrics.TagsAndMeta tq *taskqueue.TaskQueue builtinMetrics *metrics.BuiltinMetrics - obj *goja.Object // the object that is given to js to interact with the WebSocket + obj *sobek.Object // the object that is given to js to interact with the WebSocket started time.Time done chan struct{} @@ -84,6 +84,8 @@ type webSocket struct { readyState ReadyState bufferedAmount int binaryType string + protocol string + extensions []string } type ping struct { @@ -91,7 +93,15 @@ type ping struct { timestamps map[string]time.Time } -func (r *WebSocketsAPI) websocket(c goja.ConstructorCall) *goja.Object { +func isString(o *sobek.Object, rt *sobek.Runtime) bool { + return o.Prototype().Get("constructor") == rt.GlobalObject().Get("String") +} + +func isArray(o *sobek.Object, rt *sobek.Runtime) bool { + return o.Prototype().Get("constructor") == rt.GlobalObject().Get("Array") +} + +func (r *WebSocketsAPI) websocket(c sobek.ConstructorCall) *sobek.Object { rt := r.vu.Runtime() url, err := parseURL(c.Argument(0)) @@ -104,6 +114,19 @@ func (r *WebSocketsAPI) websocket(c goja.ConstructorCall) *goja.Object { common.Throw(rt, err) } + subprocotolsArg := c.Argument(1) + if !common.IsNullish(subprocotolsArg) { + subprocotolsObj := subprocotolsArg.ToObject(rt) + switch { + case isString(subprocotolsObj, rt): + params.subprocotols = append(params.subprocotols, subprocotolsObj.String()) + case isArray(subprocotolsObj, rt): + for _, key := range subprocotolsObj.Keys() { + params.subprocotols = append(params.subprocotols, subprocotolsObj.Get(key).String()) + } + } + } + w := &webSocket{ vu: r.vu, url: url, @@ -126,8 +149,8 @@ func (r *WebSocketsAPI) websocket(c goja.ConstructorCall) *goja.Object { } // parseURL parses the url from the first constructor calls argument or returns an error -func parseURL(urlValue goja.Value) (*url.URL, error) { - if urlValue == nil || goja.IsUndefined(urlValue) { +func parseURL(urlValue sobek.Value) (*url.URL, error) { + if urlValue == nil || sobek.IsUndefined(urlValue) { return nil, errors.New("WebSocket requires a url") } @@ -153,27 +176,30 @@ const ( ) // defineWebsocket defines all properties and methods for the WebSocket -func defineWebsocket(rt *goja.Runtime, w *webSocket) { +func defineWebsocket(rt *sobek.Runtime, w *webSocket) { must(rt, w.obj.DefineDataProperty( - "addEventListener", rt.ToValue(w.addEventListener), goja.FLAG_FALSE, goja.FLAG_FALSE, goja.FLAG_TRUE)) + "addEventListener", rt.ToValue(w.addEventListener), sobek.FLAG_FALSE, sobek.FLAG_FALSE, sobek.FLAG_TRUE)) must(rt, w.obj.DefineDataProperty( - "send", rt.ToValue(w.send), goja.FLAG_FALSE, goja.FLAG_FALSE, goja.FLAG_TRUE)) + "send", rt.ToValue(w.send), sobek.FLAG_FALSE, sobek.FLAG_FALSE, sobek.FLAG_TRUE)) must(rt, w.obj.DefineDataProperty( - "ping", rt.ToValue(w.ping), goja.FLAG_FALSE, goja.FLAG_FALSE, goja.FLAG_TRUE)) + "ping", rt.ToValue(w.ping), sobek.FLAG_FALSE, sobek.FLAG_FALSE, sobek.FLAG_TRUE)) must(rt, w.obj.DefineDataProperty( - "close", rt.ToValue(w.close), goja.FLAG_FALSE, goja.FLAG_FALSE, goja.FLAG_TRUE)) + "close", rt.ToValue(w.close), sobek.FLAG_FALSE, sobek.FLAG_FALSE, sobek.FLAG_TRUE)) must(rt, w.obj.DefineDataProperty( - "url", rt.ToValue(w.url.String()), goja.FLAG_FALSE, goja.FLAG_FALSE, goja.FLAG_TRUE)) + "url", rt.ToValue(w.url.String()), sobek.FLAG_FALSE, sobek.FLAG_FALSE, sobek.FLAG_TRUE)) must(rt, w.obj.DefineAccessorProperty( // this needs to be with an accessor as we change the value "readyState", rt.ToValue(func() ReadyState { return w.readyState - }), nil, goja.FLAG_FALSE, goja.FLAG_TRUE)) - must(rt, w.obj.DefineDataProperty( - "bufferedAmount", rt.ToValue(w.bufferedAmount), goja.FLAG_FALSE, goja.FLAG_FALSE, goja.FLAG_TRUE)) - // extensions - // protocol + }), nil, sobek.FLAG_FALSE, sobek.FLAG_TRUE)) + must(rt, w.obj.DefineAccessorProperty( + "bufferedAmount", rt.ToValue(func() sobek.Value { return rt.ToValue(w.bufferedAmount) }), nil, + sobek.FLAG_FALSE, sobek.FLAG_TRUE)) + must(rt, w.obj.DefineAccessorProperty("extensions", + rt.ToValue(func() sobek.Value { return rt.ToValue(w.extensions) }), nil, sobek.FLAG_FALSE, sobek.FLAG_TRUE)) must(rt, w.obj.DefineAccessorProperty( - "binaryType", rt.ToValue(func() goja.Value { + "protocol", rt.ToValue(func() sobek.Value { return rt.ToValue(w.protocol) }), nil, sobek.FLAG_FALSE, sobek.FLAG_TRUE)) + must(rt, w.obj.DefineAccessorProperty( + "binaryType", rt.ToValue(func() sobek.Value { return rt.ToValue(w.binaryType) }), rt.ToValue(func(s string) error { switch s { @@ -185,7 +211,7 @@ func defineWebsocket(rt *goja.Runtime, w *webSocket) { default: return fmt.Errorf("unknown binaryType %s, the supported one is arraybuffer", s) } - }), goja.FLAG_FALSE, goja.FLAG_TRUE)) + }), sobek.FLAG_FALSE, sobek.FLAG_TRUE)) setOn := func(property string, el *eventListener) { if el == nil { @@ -194,27 +220,27 @@ func defineWebsocket(rt *goja.Runtime, w *webSocket) { } must(rt, w.obj.DefineAccessorProperty( - property, rt.ToValue(func() goja.Value { + property, rt.ToValue(func() sobek.Value { return rt.ToValue(el.getOn) - }), rt.ToValue(func(call goja.FunctionCall) goja.Value { + }), rt.ToValue(func(call sobek.FunctionCall) sobek.Value { arg := call.Argument(0) // it's possible to unset handlers by setting them to null - if arg == nil || goja.IsUndefined(arg) || goja.IsNull(arg) { + if arg == nil || sobek.IsUndefined(arg) || sobek.IsNull(arg) { el.setOn(nil) return nil } - fn, isFunc := goja.AssertFunction(arg) + fn, isFunc := sobek.AssertFunction(arg) if !isFunc { common.Throw(rt, fmt.Errorf("a value for '%s' should be callable", property)) } - el.setOn(func(v goja.Value) (goja.Value, error) { return fn(goja.Undefined(), v) }) + el.setOn(func(v sobek.Value) (sobek.Value, error) { return fn(sobek.Undefined(), v) }) return nil - }), goja.FLAG_FALSE, goja.FLAG_TRUE)) + }), sobek.FLAG_FALSE, sobek.FLAG_TRUE)) } setOn("onmessage", w.eventListeners.getType(events.MESSAGE)) @@ -249,6 +275,7 @@ func (w *webSocket) establishConnection(params *wsParams) { Proxy: http.ProxyFromEnvironment, TLSClientConfig: tlsConfig, EnableCompression: params.enableCompression, + Subprotocols: params.subprocotols, } // this is needed because of how interfaces work and that wsd.Jar is http.Cookiejar @@ -276,8 +303,11 @@ func (w *webSocket) establishConnection(params *wsParams) { }() w.tagsAndMeta.SetSystemTagOrMetaIfEnabled(systemTags, metrics.TagStatus, strconv.Itoa(httpResponse.StatusCode)) - subProtocol := httpResponse.Header.Get("Sec-WebSocket-Protocol") - w.tagsAndMeta.SetSystemTagOrMetaIfEnabled(systemTags, metrics.TagSubproto, subProtocol) + if conn != nil { + w.protocol = conn.Subprotocol() + } + w.extensions = httpResponse.Header.Values("Sec-WebSocket-Extensions") + w.tagsAndMeta.SetSystemTagOrMetaIfEnabled(systemTags, metrics.TagSubproto, w.protocol) } w.conn = conn @@ -424,16 +454,16 @@ func (w *webSocket) queueMessage(msg *message) { } // TODO this technically could be BLOB , but we don't support that ab := rt.NewArrayBuffer(msg.data) - must(rt, ev.DefineDataProperty("data", rt.ToValue(ab), goja.FLAG_FALSE, goja.FLAG_FALSE, goja.FLAG_TRUE)) + must(rt, ev.DefineDataProperty("data", rt.ToValue(ab), sobek.FLAG_FALSE, sobek.FLAG_FALSE, sobek.FLAG_TRUE)) } else { must( rt, - ev.DefineDataProperty("data", rt.ToValue(string(msg.data)), goja.FLAG_FALSE, goja.FLAG_FALSE, goja.FLAG_TRUE), + ev.DefineDataProperty("data", rt.ToValue(string(msg.data)), sobek.FLAG_FALSE, sobek.FLAG_FALSE, sobek.FLAG_TRUE), ) } must( rt, - ev.DefineDataProperty("origin", rt.ToValue(w.url.String()), goja.FLAG_FALSE, goja.FLAG_FALSE, goja.FLAG_TRUE), + ev.DefineDataProperty("origin", rt.ToValue(w.url.String()), sobek.FLAG_FALSE, sobek.FLAG_FALSE, sobek.FLAG_TRUE), ) for _, messageListener := range w.eventListeners.all(events.MESSAGE) { @@ -558,7 +588,7 @@ func (w *webSocket) writePump(wg *sync.WaitGroup) { } } -func (w *webSocket) send(msg goja.Value) { +func (w *webSocket) send(msg sobek.Value) { w.assertStateOpen() switch o := msg.Export().(type) { @@ -569,7 +599,7 @@ func (w *webSocket) send(msg goja.Value) { data: []byte(o), t: time.Now(), } - case *goja.ArrayBuffer: + case *sobek.ArrayBuffer: b := o.Bytes() w.bufferedAmount += len(b) w.writeQueueCh <- message{ @@ -577,7 +607,7 @@ func (w *webSocket) send(msg goja.Value) { data: b, t: time.Now(), } - case goja.ArrayBuffer: + case sobek.ArrayBuffer: b := o.Bytes() w.bufferedAmount += len(b) w.writeQueueCh <- message{ @@ -692,16 +722,16 @@ func (w *webSocket) connectionClosedWithError(err error) error { // newEvent return an event implementing "implements" https://dom.spec.whatwg.org/#event // needs to be called on the event loop // TODO: move to events -func (w *webSocket) newEvent(eventType string, t time.Time) *goja.Object { +func (w *webSocket) newEvent(eventType string, t time.Time) *sobek.Object { rt := w.vu.Runtime() o := rt.NewObject() must(rt, o.DefineAccessorProperty("type", rt.ToValue(func() string { return eventType - }), nil, goja.FLAG_FALSE, goja.FLAG_TRUE)) + }), nil, sobek.FLAG_FALSE, sobek.FLAG_TRUE)) must(rt, o.DefineAccessorProperty("target", rt.ToValue(func() interface{} { return w.obj - }), nil, goja.FLAG_FALSE, goja.FLAG_TRUE)) + }), nil, sobek.FLAG_FALSE, sobek.FLAG_TRUE)) // skip srcElement // skip currentTarget ??!! // skip eventPhase ??!! @@ -713,7 +743,7 @@ func (w *webSocket) newEvent(eventType string, t time.Time) *goja.Object { must(rt, o.DefineAccessorProperty("timestamp", rt.ToValue(func() float64 { return float64(t.UnixNano()) / 1_000_000 // milliseconds as double as per the spec // https://w3c.github.io/hr-time/#dom-domhighrestimestamp - }), nil, goja.FLAG_FALSE, goja.FLAG_TRUE)) + }), nil, sobek.FLAG_FALSE, sobek.FLAG_TRUE)) return o } @@ -735,7 +765,7 @@ func (w *webSocket) callErrorListeners(e error) error { // TODO use the error ev ev := w.newEvent(events.ERROR, time.Now()) must(rt, ev.DefineDataProperty("error", rt.ToValue(e.Error()), - goja.FLAG_FALSE, goja.FLAG_FALSE, goja.FLAG_TRUE)) + sobek.FLAG_FALSE, sobek.FLAG_FALSE, sobek.FLAG_TRUE)) for _, errorListener := range w.eventListeners.all(events.ERROR) { if _, err := errorListener(ev); err != nil { // TODO fix timestamp return err @@ -754,10 +784,15 @@ func (w *webSocket) callEventListeners(eventType string) error { return nil } -func (w *webSocket) addEventListener(event string, listener func(goja.Value) (goja.Value, error)) { +func (w *webSocket) addEventListener(event string, handler func(sobek.Value) (sobek.Value, error)) { // TODO support options https://developer.mozilla.org/en-US/docs/Web/API/EventTarget/addEventListener#parameters - if err := w.eventListeners.add(event, listener); err != nil { - w.vu.State().Logger.Warnf("can't add event listener: %s", err) + + if handler == nil { + common.Throw(w.vu.Runtime(), fmt.Errorf("handler for event type %q isn't a callable function", event)) + } + + if err := w.eventListeners.add(event, handler); err != nil { + w.vu.State().Logger.Warnf("can't add event handler: %s", err) } } diff --git a/vendor/modules.txt b/vendor/modules.txt index 89a220e5aa3..e20c2f36189 100644 --- a/vendor/modules.txt +++ b/vendor/modules.txt @@ -203,8 +203,8 @@ github.com/grafana/xk6-redis/redis # github.com/grafana/xk6-webcrypto v0.3.0 ## explicit; go 1.19 github.com/grafana/xk6-webcrypto/webcrypto -# github.com/grafana/xk6-websockets v0.4.0 -## explicit; go 1.18 +# github.com/grafana/xk6-websockets v0.5.1 +## explicit; go 1.20 github.com/grafana/xk6-websockets/websockets github.com/grafana/xk6-websockets/websockets/events # github.com/grpc-ecosystem/go-grpc-middleware v1.4.0