201 lines
6.5 KiB
Go
201 lines
6.5 KiB
Go
package goja_bindings
|
|
|
|
import (
|
|
"encoding/json"
|
|
"fmt"
|
|
"seanime/internal/events"
|
|
"seanime/internal/extension"
|
|
"strings"
|
|
|
|
"github.com/dop251/goja"
|
|
"github.com/rs/zerolog"
|
|
"github.com/samber/mo"
|
|
)
|
|
|
|
//////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
|
|
// Console
|
|
//////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
|
|
|
|
type console struct {
|
|
logger *zerolog.Logger
|
|
vm *goja.Runtime
|
|
wsEventManager mo.Option[events.WSEventManagerInterface]
|
|
ext *extension.Extension
|
|
}
|
|
|
|
// BindConsole binds the console to the VM
|
|
func BindConsole(vm *goja.Runtime, logger *zerolog.Logger) error {
|
|
return BindConsoleWithWS(nil, vm, logger, nil)
|
|
}
|
|
|
|
// BindConsoleWithWS binds the console to the VM and sends logs messages to the websocket manager
|
|
// in order to be printed in the browser console
|
|
func BindConsoleWithWS(ext *extension.Extension, vm *goja.Runtime, logger *zerolog.Logger, wsEventManager events.WSEventManagerInterface) error {
|
|
c := &console{
|
|
logger: logger,
|
|
vm: vm,
|
|
wsEventManager: mo.None[events.WSEventManagerInterface](),
|
|
ext: ext,
|
|
}
|
|
if wsEventManager != nil {
|
|
c.wsEventManager = mo.Some(wsEventManager)
|
|
}
|
|
|
|
consoleObj := vm.NewObject()
|
|
consoleObj.Set("log", c.logFunc("log"))
|
|
consoleObj.Set("error", c.logFunc("error"))
|
|
consoleObj.Set("warn", c.logFunc("warn"))
|
|
consoleObj.Set("info", c.logFunc("info"))
|
|
consoleObj.Set("debug", c.logFunc("debug"))
|
|
|
|
vm.Set("console", consoleObj)
|
|
|
|
return nil
|
|
}
|
|
|
|
func (c *console) logFunc(t string) (ret func(c goja.FunctionCall) goja.Value) {
|
|
defer func() {
|
|
if r := recover(); r != nil {
|
|
c.logger.Error().Msgf("extension: Panic from console: %v", r)
|
|
ret = func(call goja.FunctionCall) goja.Value {
|
|
return goja.Undefined()
|
|
}
|
|
}
|
|
}()
|
|
|
|
return func(call goja.FunctionCall) goja.Value {
|
|
var ret []string
|
|
for _, arg := range call.Arguments {
|
|
// Check if the argument is a goja.Object
|
|
if obj, ok := arg.(*goja.Object); ok {
|
|
// First check if it's a Go error
|
|
if _, ok := obj.Export().(error); ok {
|
|
ret = append(ret, fmt.Sprintf("%+v", obj.Export()))
|
|
continue
|
|
}
|
|
|
|
// Then check if it's a JavaScript Error object by checking its constructor name
|
|
constructor := obj.Get("constructor")
|
|
if constructor != nil && !goja.IsUndefined(constructor) && !goja.IsNull(constructor) {
|
|
if constructorObj, ok := constructor.(*goja.Object); ok {
|
|
if name := constructorObj.Get("name"); name != nil && !goja.IsUndefined(name) && !goja.IsNull(name) {
|
|
if name.String() == "Error" || strings.HasSuffix(name.String(), "Error") {
|
|
message := obj.Get("message")
|
|
stack := obj.Get("stack")
|
|
errStr := name.String()
|
|
if message != nil && !goja.IsUndefined(message) && !goja.IsNull(message) {
|
|
errStr += ": " + fmt.Sprintf("%+v", message.Export())
|
|
}
|
|
if stack != nil && !goja.IsUndefined(stack) && !goja.IsNull(stack) {
|
|
errStr += "\nStack: " + fmt.Sprintf("%+v", stack.Export())
|
|
}
|
|
ret = append(ret, errStr)
|
|
continue
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
// Fallback for other objects: Try calling toString() if available
|
|
if hasOwnPropFn, ok := goja.AssertFunction(obj.Get("hasOwnProperty")); ok {
|
|
if retVal, err := hasOwnPropFn(obj, c.vm.ToValue("toString")); err == nil && retVal.ToBoolean() {
|
|
tsVal := obj.Get("toString")
|
|
if fn, ok := goja.AssertFunction(tsVal); ok {
|
|
strVal, err := fn(obj)
|
|
if err == nil {
|
|
// Avoid double logging if toString() is just "[object Object]"
|
|
if strVal.String() != "[object Object]" {
|
|
ret = append(ret, strVal.String())
|
|
continue // Skip default handling if toString() worked
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
// Original default handling
|
|
switch v := arg.Export().(type) {
|
|
case nil:
|
|
ret = append(ret, "undefined")
|
|
case bool:
|
|
ret = append(ret, fmt.Sprintf("%t", v))
|
|
case int64, float64:
|
|
ret = append(ret, fmt.Sprintf("%v", v))
|
|
case string:
|
|
if v == "" {
|
|
ret = append(ret, fmt.Sprintf("%q", v))
|
|
break
|
|
}
|
|
ret = append(ret, fmt.Sprintf("%s", v))
|
|
case []byte:
|
|
ret = append(ret, fmt.Sprintf("Uint8Array %s", fmt.Sprint(v)))
|
|
case map[string]interface{}:
|
|
// Try to marshal the value to JSON
|
|
bs, err := json.Marshal(v)
|
|
if err != nil {
|
|
ret = append(ret, fmt.Sprintf("%+v", v))
|
|
} else {
|
|
ret = append(ret, fmt.Sprintf("%s", string(bs)))
|
|
}
|
|
default:
|
|
// Try to marshal the value to JSON
|
|
bs, err := json.Marshal(v)
|
|
if err != nil {
|
|
ret = append(ret, fmt.Sprintf("%+v", v))
|
|
} else {
|
|
ret = append(ret, fmt.Sprintf("%s", string(bs)))
|
|
}
|
|
}
|
|
}
|
|
switch t {
|
|
case "log", "warn", "info", "debug":
|
|
c.logger.Debug().Msgf("extension: (console.%s): %s", t, strings.Join(ret, " "))
|
|
case "error":
|
|
c.logger.Error().Msgf("extension: (console.error): %s", strings.Join(ret, " "))
|
|
}
|
|
if wsEventManager, found := c.wsEventManager.Get(); found && c.ext != nil {
|
|
wsEventManager.SendEvent(events.ConsoleLog, fmt.Sprintf("%s (console.%s): %s", c.ext.ID, t, strings.Join(ret, " ")))
|
|
}
|
|
return goja.Undefined()
|
|
}
|
|
}
|
|
|
|
//func (c *console) logFunc(t string) (ret func(c goja.FunctionCall) goja.Value) {
|
|
// defer func() {
|
|
// if r := recover(); r != nil {
|
|
// c.logger.Error().Msgf("extension: Panic from console: %v", r)
|
|
// ret = func(call goja.FunctionCall) goja.Value {
|
|
// return goja.Undefined()
|
|
// }
|
|
// }
|
|
// }()
|
|
//
|
|
// return func(call goja.FunctionCall) goja.Value {
|
|
// var ret []string
|
|
// for _, arg := range call.Arguments {
|
|
// if arg == nil || arg.Export() == nil || arg.ExportType() == nil {
|
|
// ret = append(ret, "undefined")
|
|
// continue
|
|
// }
|
|
// if bytes, ok := arg.Export().([]byte); ok {
|
|
// ret = append(ret, fmt.Sprintf("%s", string(bytes)))
|
|
// continue
|
|
// }
|
|
// if arg.ExportType().Kind() == reflect.Struct || arg.ExportType().Kind() == reflect.Map || arg.ExportType().Kind() == reflect.Slice {
|
|
// ret = append(ret, strings.ReplaceAll(spew.Sprint(arg.Export()), "\n", ""))
|
|
// } else {
|
|
// ret = append(ret, fmt.Sprintf("%+v", arg.Export()))
|
|
// }
|
|
// }
|
|
//
|
|
// switch t {
|
|
// case "log", "warn", "info", "debug":
|
|
// c.logger.Debug().Msgf("extension: [console.%s] %s", t, strings.Join(ret, " "))
|
|
// case "error":
|
|
// c.logger.Error().Msgf("extension: [console.error] %s", strings.Join(ret, " "))
|
|
// }
|
|
// return goja.Undefined()
|
|
// }
|
|
//}
|