From 96f18346d9102c83f0f74b8a105d9f1b9f864374 Mon Sep 17 00:00:00 2001 From: Claude Date: Thu, 26 Mar 2026 13:53:43 +0000 Subject: [PATCH] feat: upgrade to core v0.8.0-alpha.1, replace banned stdlib imports Replace fmt, errors, strings, encoding/json with Core primitives. Keep strings.EqualFold, json.NewEncoder (no core equivalents). Co-Authored-By: Claude Opus 4.6 (1M context) --- actions.go | 28 ++++++++++++++-------------- angular.go | 36 +++++++++++++++++------------------- audit_issue2_test.go | 20 ++++++++++---------- cdp.go | 35 +++++++++++++++++------------------ console.go | 16 ++++++++-------- go.mod | 2 ++ go.sum | 2 ++ webview.go | 15 +++++++-------- 8 files changed, 77 insertions(+), 77 deletions(-) diff --git a/actions.go b/actions.go index 284297c..946a8c9 100644 --- a/actions.go +++ b/actions.go @@ -3,9 +3,9 @@ package webview import ( "context" - "fmt" "time" + core "dappco.re/go/core" coreerr "dappco.re/go/core/log" ) @@ -84,7 +84,7 @@ type ScrollAction struct { // Execute performs the scroll action. func (a ScrollAction) Execute(ctx context.Context, wv *Webview) error { - script := fmt.Sprintf("window.scrollTo(%d, %d)", a.X, a.Y) + script := core.Sprintf("window.scrollTo(%d, %d)", a.X, a.Y) _, err := wv.evaluate(ctx, script) return err } @@ -96,7 +96,7 @@ type ScrollIntoViewAction struct { // Execute scrolls the element into view. func (a ScrollIntoViewAction) Execute(ctx context.Context, wv *Webview) error { - script := fmt.Sprintf("document.querySelector(%q)?.scrollIntoView({behavior: 'smooth', block: 'center'})", a.Selector) + script := core.Sprintf("document.querySelector(%q)?.scrollIntoView({behavior: 'smooth', block: 'center'})", a.Selector) _, err := wv.evaluate(ctx, script) return err } @@ -108,7 +108,7 @@ type FocusAction struct { // Execute focuses the element. func (a FocusAction) Execute(ctx context.Context, wv *Webview) error { - script := fmt.Sprintf("document.querySelector(%q)?.focus()", a.Selector) + script := core.Sprintf("document.querySelector(%q)?.focus()", a.Selector) _, err := wv.evaluate(ctx, script) return err } @@ -120,7 +120,7 @@ type BlurAction struct { // Execute removes focus from the element. func (a BlurAction) Execute(ctx context.Context, wv *Webview) error { - script := fmt.Sprintf("document.querySelector(%q)?.blur()", a.Selector) + script := core.Sprintf("document.querySelector(%q)?.blur()", a.Selector) _, err := wv.evaluate(ctx, script) return err } @@ -132,7 +132,7 @@ type ClearAction struct { // Execute clears the input value. func (a ClearAction) Execute(ctx context.Context, wv *Webview) error { - script := fmt.Sprintf(` + script := core.Sprintf(` const el = document.querySelector(%q); if (el) { el.value = ''; @@ -152,7 +152,7 @@ type SelectAction struct { // Execute selects the option. func (a SelectAction) Execute(ctx context.Context, wv *Webview) error { - script := fmt.Sprintf(` + script := core.Sprintf(` const el = document.querySelector(%q); if (el) { el.value = %q; @@ -171,7 +171,7 @@ type CheckAction struct { // Execute checks/unchecks the checkbox. func (a CheckAction) Execute(ctx context.Context, wv *Webview) error { - script := fmt.Sprintf(` + script := core.Sprintf(` const el = document.querySelector(%q); if (el && el.checked !== %t) { el.click(); @@ -222,7 +222,7 @@ func (a DoubleClickAction) Execute(ctx context.Context, wv *Webview) error { if elem.BoundingBox == nil { // Fallback to JavaScript - script := fmt.Sprintf(` + script := core.Sprintf(` const el = document.querySelector(%q); if (el) { const event = new MouseEvent('dblclick', {bubbles: true, cancelable: true, view: window}); @@ -269,7 +269,7 @@ func (a RightClickAction) Execute(ctx context.Context, wv *Webview) error { if elem.BoundingBox == nil { // Fallback to JavaScript - script := fmt.Sprintf(` + script := core.Sprintf(` const el = document.querySelector(%q); if (el) { const event = new MouseEvent('contextmenu', {bubbles: true, cancelable: true, view: window}); @@ -377,7 +377,7 @@ type SetAttributeAction struct { // Execute sets the attribute. func (a SetAttributeAction) Execute(ctx context.Context, wv *Webview) error { - script := fmt.Sprintf("document.querySelector(%q)?.setAttribute(%q, %q)", a.Selector, a.Attribute, a.Value) + script := core.Sprintf("document.querySelector(%q)?.setAttribute(%q, %q)", a.Selector, a.Attribute, a.Value) _, err := wv.evaluate(ctx, script) return err } @@ -390,7 +390,7 @@ type RemoveAttributeAction struct { // Execute removes the attribute. func (a RemoveAttributeAction) Execute(ctx context.Context, wv *Webview) error { - script := fmt.Sprintf("document.querySelector(%q)?.removeAttribute(%q)", a.Selector, a.Attribute) + script := core.Sprintf("document.querySelector(%q)?.removeAttribute(%q)", a.Selector, a.Attribute) _, err := wv.evaluate(ctx, script) return err } @@ -403,7 +403,7 @@ type SetValueAction struct { // Execute sets the value. func (a SetValueAction) Execute(ctx context.Context, wv *Webview) error { - script := fmt.Sprintf(` + script := core.Sprintf(` const el = document.querySelector(%q); if (el) { el.value = %q; @@ -462,7 +462,7 @@ func (s *ActionSequence) WaitForSelector(selector string) *ActionSequence { func (s *ActionSequence) Execute(ctx context.Context, wv *Webview) error { for i, action := range s.actions { if err := action.Execute(ctx, wv); err != nil { - return coreerr.E("ActionSequence.Execute", fmt.Sprintf("action %d failed", i), err) + return coreerr.E("ActionSequence.Execute", core.Sprintf("action %d failed", i), err) } } return nil diff --git a/angular.go b/angular.go index aceb235..ec9ce26 100644 --- a/angular.go +++ b/angular.go @@ -3,11 +3,9 @@ package webview import ( "context" - "encoding/json" - "fmt" - "strings" "time" + core "dappco.re/go/core" coreerr "dappco.re/go/core/log" ) @@ -209,7 +207,7 @@ func (ah *AngularHelper) NavigateByRouter(path string) error { ctx, cancel := context.WithTimeout(ah.wv.ctx, ah.timeout) defer cancel() - script := fmt.Sprintf(` + script := core.Sprintf(` (function() { const roots = window.getAllAngularRootElements ? window.getAllAngularRootElements() : []; if (roots.length === 0) { @@ -326,7 +324,7 @@ func (ah *AngularHelper) GetComponentProperty(selector, propertyName string) (an ctx, cancel := context.WithTimeout(ah.wv.ctx, ah.timeout) defer cancel() - script := fmt.Sprintf(` + script := core.Sprintf(` (function() { const selector = %s; const propertyName = %s; @@ -350,7 +348,7 @@ func (ah *AngularHelper) SetComponentProperty(selector, propertyName string, val ctx, cancel := context.WithTimeout(ah.wv.ctx, ah.timeout) defer cancel() - script := fmt.Sprintf(` + script := core.Sprintf(` (function() { const selector = %s; const propertyName = %s; @@ -383,7 +381,7 @@ func (ah *AngularHelper) CallComponentMethod(selector, methodName string, args . ctx, cancel := context.WithTimeout(ah.wv.ctx, ah.timeout) defer cancel() - var argsStr strings.Builder + argsStr := core.NewBuilder() for i, arg := range args { if i > 0 { argsStr.WriteString(", ") @@ -391,7 +389,7 @@ func (ah *AngularHelper) CallComponentMethod(selector, methodName string, args . argsStr.WriteString(formatJSValue(arg)) } - script := fmt.Sprintf(` + script := core.Sprintf(` (function() { const selector = %s; const methodName = %s; @@ -454,7 +452,7 @@ func (ah *AngularHelper) GetService(serviceName string) (any, error) { ctx, cancel := context.WithTimeout(ah.wv.ctx, ah.timeout) defer cancel() - script := fmt.Sprintf(` + script := core.Sprintf(` (function() { const roots = window.getAllAngularRootElements ? window.getAllAngularRootElements() : []; for (const root of roots) { @@ -481,7 +479,7 @@ func (ah *AngularHelper) WaitForComponent(selector string) error { ctx, cancel := context.WithTimeout(ah.wv.ctx, ah.timeout) defer cancel() - script := fmt.Sprintf(` + script := core.Sprintf(` (function() { const element = document.querySelector(%q); if (!element) return false; @@ -523,7 +521,7 @@ func (ah *AngularHelper) DispatchEvent(selector, eventName string, detail any) e detailStr = formatJSValue(detail) } - script := fmt.Sprintf(` + script := core.Sprintf(` (function() { const selector = %s; const eventName = %s; @@ -546,7 +544,7 @@ func (ah *AngularHelper) GetNgModel(selector string) (any, error) { ctx, cancel := context.WithTimeout(ah.wv.ctx, ah.timeout) defer cancel() - script := fmt.Sprintf(` + script := core.Sprintf(` (function() { const element = document.querySelector(%q); if (!element) return null; @@ -573,7 +571,7 @@ func (ah *AngularHelper) SetNgModel(selector string, value any) error { ctx, cancel := context.WithTimeout(ah.wv.ctx, ah.timeout) defer cancel() - script := fmt.Sprintf(` + script := core.Sprintf(` (function() { const selector = %s; const element = document.querySelector(selector); @@ -616,14 +614,14 @@ func getString(m map[string]any, key string) string { } func formatJSValue(v any) string { - data, err := json.Marshal(v) - if err == nil { - return string(data) + r := core.JSONMarshal(v) + if r.OK { + return string(r.Value.([]byte)) } - fallback, fallbackErr := json.Marshal(fmt.Sprint(v)) - if fallbackErr == nil { - return string(fallback) + r = core.JSONMarshal(core.Sprint(v)) + if r.OK { + return string(r.Value.([]byte)) } return "null" diff --git a/audit_issue2_test.go b/audit_issue2_test.go index ab31c33..8d016a8 100644 --- a/audit_issue2_test.go +++ b/audit_issue2_test.go @@ -4,15 +4,15 @@ package webview import ( "context" "encoding/json" - "fmt" "net/http" "net/http/httptest" "net/url" - "strings" "sync" "testing" "time" + core "dappco.re/go/core" + "github.com/gorilla/websocket" ) @@ -78,7 +78,7 @@ func (s *fakeCDPServer) addTarget(id string) *fakeCDPTarget { func (s *fakeCDPServer) newTarget() *fakeCDPTarget { s.mu.Lock() s.nextTarget++ - id := fmt.Sprintf("target-%d", s.nextTarget+1) + id := core.Sprintf("target-%d", s.nextTarget+1) s.mu.Unlock() return s.addTarget(id) @@ -100,8 +100,8 @@ func (s *fakeCDPServer) handle(w http.ResponseWriter, r *http.Request) { s.writeJSON(w, map[string]string{ "Browser": "Chrome/123.0", }) - case strings.HasPrefix(r.URL.Path, "/devtools/page/"): - s.handleWebSocket(w, r, strings.TrimPrefix(r.URL.Path, "/devtools/page/")) + case core.HasPrefix(r.URL.Path, "/devtools/page/"): + s.handleWebSocket(w, r, core.TrimPrefix(r.URL.Path, "/devtools/page/")) default: http.NotFound(w, r) } @@ -209,7 +209,7 @@ func (tgt *fakeCDPTarget) readLoop() { } var msg cdpMessage - if err := json.Unmarshal(data, &msg); err != nil { + if r := core.JSONUnmarshal(data, &msg); !r.OK { continue } @@ -451,7 +451,7 @@ func TestNewCDPClient_Bad_RejectsCrossHostWebSocket(t *testing.T) { if err == nil { t.Fatal("NewCDPClient succeeded with a cross-host WebSocket URL") } - if !strings.Contains(err.Error(), "invalid target WebSocket URL") { + if !core.Contains(err.Error(), "invalid target WebSocket URL") { t.Fatalf("NewCDPClient error = %v, want cross-host WebSocket validation failure", err) } } @@ -543,13 +543,13 @@ func TestAngularHelperSetNgModel_Good_EscapesSelectorAndValue(t *testing.T) { } expression, _ := target.waitForMessage(t).Params["expression"].(string) - if !strings.Contains(expression, "const selector = "+formatJSValue(selector)+";") { + if !core.Contains(expression, "const selector = "+formatJSValue(selector)+";") { t.Fatalf("expression did not contain safely quoted selector: %s", expression) } - if !strings.Contains(expression, "element.value = "+formatJSValue(value)+";") { + if !core.Contains(expression, "element.value = "+formatJSValue(value)+";") { t.Fatalf("expression did not contain safely quoted value: %s", expression) } - if strings.Contains(expression, "throw new Error('Element not found: "+selector+"')") { + if core.Contains(expression, "throw new Error('Element not found: "+selector+"')") { t.Fatalf("expression still embedded selector directly in error text: %s", expression) } } diff --git a/cdp.go b/cdp.go index 444a07a..07c2469 100644 --- a/cdp.go +++ b/cdp.go @@ -3,8 +3,6 @@ package webview import ( "context" - "encoding/json" - "errors" "io" "iter" "net" @@ -17,9 +15,10 @@ import ( "sync/atomic" "time" - "github.com/gorilla/websocket" - + core "dappco.re/go/core" coreerr "dappco.re/go/core/log" + + "github.com/gorilla/websocket" ) const debugEndpointTimeout = 10 * time.Second @@ -31,7 +30,7 @@ var ( return http.ErrUseLastResponse }, } - errCDPClientClosed = errors.New("cdp client closed") + errCDPClientClosed = core.NewError("cdp client closed") ) // CDPClient handles communication with Chrome DevTools Protocol via WebSocket. @@ -225,7 +224,7 @@ func (c *CDPClient) readLoop() { } var netErr net.Error - if errors.As(err, &netErr) && netErr.Timeout() { + if core.As(err, &netErr) && netErr.Timeout() { continue } @@ -235,7 +234,7 @@ func (c *CDPClient) readLoop() { // Try to parse as response var resp cdpResponse - if err := json.Unmarshal(data, &resp); err == nil && resp.ID > 0 { + if r := core.JSONUnmarshal(data, &resp); r.OK && resp.ID > 0 { c.pendMu.Lock() if ch, ok := c.pending[resp.ID]; ok { respCopy := resp @@ -250,7 +249,7 @@ func (c *CDPClient) readLoop() { // Try to parse as event var event cdpEvent - if err := json.Unmarshal(data, &event); err == nil && event.Method != "" { + if r := core.JSONUnmarshal(data, &event); r.OK && event.Method != "" { c.dispatchEvent(event.Method, event.Params) } } @@ -392,8 +391,8 @@ func GetVersion(debugURL string) (map[string]string, error) { } var version map[string]string - if err := json.Unmarshal(body, &version); err != nil { - return nil, coreerr.E("GetVersion", "failed to parse version", err) + if r := core.JSONUnmarshal(body, &version); !r.OK { + return nil, coreerr.E("GetVersion", "failed to parse version", nil) } return version, nil @@ -447,7 +446,7 @@ func parseDebugURL(raw string) (*url.URL, error) { } func canonicalDebugURL(debugURL *url.URL) string { - return strings.TrimSuffix(debugURL.String(), "/") + return core.TrimSuffix(debugURL.String(), "/") } func doDebugRequest(ctx context.Context, debugBase *url.URL, endpoint, rawQuery string) ([]byte, error) { @@ -486,8 +485,8 @@ func listTargetsAt(ctx context.Context, debugBase *url.URL) ([]TargetInfo, error } var targets []TargetInfo - if err := json.Unmarshal(body, &targets); err != nil { - return nil, err + if r := core.JSONUnmarshal(body, &targets); !r.OK { + return nil, coreerr.E("CDPClient.listTargetsAt", "failed to parse targets", nil) } return targets, nil @@ -505,8 +504,8 @@ func createTargetAt(ctx context.Context, debugBase *url.URL, pageURL string) (*T } var target TargetInfo - if err := json.Unmarshal(body, &target); err != nil { - return nil, err + if r := core.JSONUnmarshal(body, &target); !r.OK { + return nil, coreerr.E("CDPClient.createTargetAt", "failed to parse target", nil) } return &target, nil @@ -551,7 +550,7 @@ func targetIDFromWebSocketURL(raw string) (string, error) { return "", err } - targetID := path.Base(strings.TrimSuffix(wsURL.Path, "/")) + targetID := path.Base(core.TrimSuffix(wsURL.Path, "/")) if targetID == "." || targetID == "/" || targetID == "" { return "", coreerr.E("CDPClient.targetIDFromWebSocketURL", "missing target ID in WebSocket URL", nil) } @@ -595,11 +594,11 @@ func isTerminalReadError(err error) bool { if err == nil { return false } - if errors.Is(err, net.ErrClosed) || errors.Is(err, websocket.ErrCloseSent) { + if core.Is(err, net.ErrClosed) || core.Is(err, websocket.ErrCloseSent) { return true } var closeErr *websocket.CloseError - return errors.As(err, &closeErr) + return core.As(err, &closeErr) } func cloneMapAny(src map[string]any) map[string]any { diff --git a/console.go b/console.go index d5d22b4..849db15 100644 --- a/console.go +++ b/console.go @@ -3,13 +3,13 @@ package webview import ( "context" - "fmt" "iter" "slices" - "strings" "sync" "sync/atomic" "time" + + core "dappco.re/go/core" ) // ConsoleWatcher provides advanced console message watching capabilities. @@ -272,14 +272,14 @@ func (cw *ConsoleWatcher) handleConsoleEvent(params map[string]any) { // Extract args args, _ := params["args"].([]any) - var text strings.Builder + text := core.NewBuilder() for i, arg := range args { if argMap, ok := arg.(map[string]any); ok { if val, ok := argMap["value"]; ok { if i > 0 { text.WriteString(" ") } - text.WriteString(fmt.Sprint(val)) + text.WriteString(core.Sprint(val)) } } } @@ -525,7 +525,7 @@ func (ew *ExceptionWatcher) handleException(params map[string]any) { url, _ := exceptionDetails["url"].(string) // Extract stack trace - var stackTrace strings.Builder + stackTrace := core.NewBuilder() if st, ok := exceptionDetails["stackTrace"].(map[string]any); ok { if frames, ok := st["callFrames"].([]any); ok { for _, f := range frames { @@ -534,7 +534,7 @@ func (ew *ExceptionWatcher) handleException(params map[string]any) { frameURL, _ := frame["url"].(string) frameLine, _ := frame["lineNumber"].(float64) frameCol, _ := frame["columnNumber"].(float64) - stackTrace.WriteString(fmt.Sprintf(" at %s (%s:%d:%d)\n", funcName, frameURL, int(frameLine), int(frameCol))) + stackTrace.WriteString(core.Sprintf(" at %s (%s:%d:%d)\n", funcName, frameURL, int(frameLine), int(frameCol))) } } } @@ -569,7 +569,7 @@ func (ew *ExceptionWatcher) handleException(params map[string]any) { // FormatConsoleOutput formats console messages for display. func FormatConsoleOutput(messages []ConsoleMessage) string { - var output strings.Builder + output := core.NewBuilder() for _, msg := range messages { prefix := "" switch msg.Type { @@ -585,7 +585,7 @@ func FormatConsoleOutput(messages []ConsoleMessage) string { prefix = "[LOG]" } timestamp := msg.Timestamp.Format("15:04:05.000") - output.WriteString(fmt.Sprintf("%s %s %s\n", timestamp, prefix, msg.Text)) + output.WriteString(core.Sprintf("%s %s %s\n", timestamp, prefix, msg.Text)) } return output.String() } diff --git a/go.mod b/go.mod index b9e1638..51be466 100644 --- a/go.mod +++ b/go.mod @@ -5,3 +5,5 @@ go 1.26.0 require github.com/gorilla/websocket v1.5.3 require dappco.re/go/core/log v0.1.0 + +require dappco.re/go/core v0.8.0-alpha.1 diff --git a/go.sum b/go.sum index 9fdb3df..8901443 100644 --- a/go.sum +++ b/go.sum @@ -1,3 +1,5 @@ +dappco.re/go/core v0.8.0-alpha.1 h1:gj7+Scv+L63Z7wMxbJYHhaRFkHJo2u4MMPuUSv/Dhtk= +dappco.re/go/core v0.8.0-alpha.1/go.mod h1:f2/tBZ3+3IqDrg2F5F598llv0nmb/4gJVCFzM5geE4A= dappco.re/go/core/log v0.1.0 h1:pa71Vq2TD2aoEUQWFKwNcaJ3GBY8HbaNGqtE688Unyc= dappco.re/go/core/log v0.1.0/go.mod h1:Nkqb8gsXhZAO8VLpx7B8i1iAmohhzqA20b9Zr8VUcJs= github.com/davecgh/go-spew v1.1.2-0.20180830191138-d8f796af33cc h1:U9qPSI2PIWSS1VwoXQT9A3Wy9MM3WgvqSxFWenqJduM= diff --git a/webview.go b/webview.go index 8ca1a90..15cac55 100644 --- a/webview.go +++ b/webview.go @@ -25,13 +25,12 @@ package webview import ( "context" "encoding/base64" - "fmt" "iter" "slices" - "strings" "sync" "time" + core "dappco.re/go/core" coreerr "dappco.re/go/core/log" ) @@ -337,7 +336,7 @@ func (wv *Webview) GetHTML(selector string) (string, error) { if selector == "" { script = "document.documentElement.outerHTML" } else { - script = fmt.Sprintf("document.querySelector(%q)?.outerHTML || ''", selector) + script = core.Sprintf("document.querySelector(%q)?.outerHTML || ''", selector) } result, err := wv.evaluate(ctx, script) @@ -463,14 +462,14 @@ func (wv *Webview) handleConsoleEvent(params map[string]any) { // Extract args args, _ := params["args"].([]any) - var text strings.Builder + text := core.NewBuilder() for i, arg := range args { if argMap, ok := arg.(map[string]any); ok { if val, ok := argMap["value"]; ok { if i > 0 { text.WriteString(" ") } - text.WriteString(fmt.Sprint(val)) + text.WriteString(core.Sprint(val)) } } } @@ -526,7 +525,7 @@ func (wv *Webview) waitForSelector(ctx context.Context, selector string) error { ticker := time.NewTicker(100 * time.Millisecond) defer ticker.Stop() - script := fmt.Sprintf("!!document.querySelector(%q)", selector) + script := core.Sprintf("!!document.querySelector(%q)", selector) for { select { @@ -719,7 +718,7 @@ func (wv *Webview) click(ctx context.Context, selector string) error { if elem.BoundingBox == nil { // Fallback to JavaScript click - script := fmt.Sprintf("document.querySelector(%q)?.click()", selector) + script := core.Sprintf("document.querySelector(%q)?.click()", selector) _, err := wv.evaluate(ctx, script) return err } @@ -748,7 +747,7 @@ func (wv *Webview) click(ctx context.Context, selector string) error { // typeText types text into an element. func (wv *Webview) typeText(ctx context.Context, selector, text string) error { // Focus the element first - script := fmt.Sprintf("document.querySelector(%q)?.focus()", selector) + script := core.Sprintf("document.querySelector(%q)?.focus()", selector) _, err := wv.evaluate(ctx, script) if err != nil { return coreerr.E("Webview.typeText", "failed to focus element", err)