yao/trace/jsapi/node.go
Max c25fbb22d7 Add trace management to context and enhance configuration options
- Introduced a trace manager in the context struct for lazy initialization and management of trace operations.
- Implemented the Trace method in the context to facilitate trace object creation and management within JavaScript.
- Enhanced configuration options for trace drivers, including defaults for local and store drivers, and added path and prefix settings.
- Updated related tests to verify the functionality of the new trace management features.
2025-11-19 12:27:42 +08:00

308 lines
8.2 KiB
Go

package jsapi
import (
"fmt"
"github.com/yaoapp/gou/runtime/v8/bridge"
"github.com/yaoapp/yao/trace/types"
"rogchap.com/v8go"
)
// Helper functions
func parseTraceInput(obj *v8go.Object, ctx *v8go.Context) (types.TraceInput, error) {
goVal, err := bridge.GoValue(obj.Value, ctx)
if err != nil {
return nil, err
}
return goVal, nil
}
func parseTraceNodeOption(obj *v8go.Object) types.TraceNodeOption {
option := types.TraceNodeOption{}
if labelVal, err := obj.Get("label"); err == nil && !labelVal.IsNullOrUndefined() {
option.Label = labelVal.String()
}
if iconVal, err := obj.Get("icon"); err == nil && !iconVal.IsNullOrUndefined() {
option.Icon = iconVal.String()
}
if descVal, err := obj.Get("description"); err == nil && !descVal.IsNullOrUndefined() {
option.Description = descVal.String()
}
return option
}
// NewNodeObject creates a JavaScript Node object (pure JS object, no Go mapping)
func NewNodeObject(v8ctx *v8go.Context, node types.Node) (*v8go.Value, error) {
jsObject := v8go.NewObjectTemplate(v8ctx.Isolate())
// Set primitive fields
jsObject.Set("id", node.ID())
// Set methods
jsObject.Set("Info", nodeInfoMethod(v8ctx.Isolate(), node))
jsObject.Set("Debug", nodeDebugMethod(v8ctx.Isolate(), node))
jsObject.Set("Error", nodeErrorMethod(v8ctx.Isolate(), node))
jsObject.Set("Warn", nodeWarnMethod(v8ctx.Isolate(), node))
jsObject.Set("Add", nodeAddMethod(v8ctx.Isolate(), node))
jsObject.Set("Parallel", nodeParallelMethod(v8ctx.Isolate(), node))
jsObject.Set("SetOutput", nodeSetOutputMethod(v8ctx.Isolate(), node))
jsObject.Set("SetMetadata", nodeSetMetadataMethod(v8ctx.Isolate(), node))
jsObject.Set("Complete", nodeCompleteMethod(v8ctx.Isolate(), node))
jsObject.Set("Fail", nodeFailMethod(v8ctx.Isolate(), node))
// Create instance
instance, err := jsObject.NewInstance(v8ctx)
if err != nil {
return nil, err
}
return instance.Value, nil
}
// Node method templates
func nodeInfoMethod(iso *v8go.Isolate, node types.Node) *v8go.FunctionTemplate {
return v8go.NewFunctionTemplate(iso, func(info *v8go.FunctionCallbackInfo) *v8go.Value {
args := info.Args()
if len(args) > 0 {
node.Info(args[0].String())
}
return info.This().Value
})
}
func nodeDebugMethod(iso *v8go.Isolate, node types.Node) *v8go.FunctionTemplate {
return v8go.NewFunctionTemplate(iso, func(info *v8go.FunctionCallbackInfo) *v8go.Value {
args := info.Args()
if len(args) > 0 {
node.Debug(args[0].String())
}
return info.This().Value
})
}
func nodeErrorMethod(iso *v8go.Isolate, node types.Node) *v8go.FunctionTemplate {
return v8go.NewFunctionTemplate(iso, func(info *v8go.FunctionCallbackInfo) *v8go.Value {
args := info.Args()
if len(args) > 0 {
node.Error(args[0].String())
}
return info.This().Value
})
}
func nodeWarnMethod(iso *v8go.Isolate, node types.Node) *v8go.FunctionTemplate {
return v8go.NewFunctionTemplate(iso, func(info *v8go.FunctionCallbackInfo) *v8go.Value {
args := info.Args()
if len(args) > 0 {
node.Warn(args[0].String())
}
return info.This().Value
})
}
func nodeAddMethod(iso *v8go.Isolate, node types.Node) *v8go.FunctionTemplate {
return v8go.NewFunctionTemplate(iso, func(info *v8go.FunctionCallbackInfo) *v8go.Value {
ctx := info.Context()
args := info.Args()
if len(args) < 2 {
return bridge.JsException(ctx, "Add requires 2 arguments: (input, option)")
}
// Parse input
inputObj, err := args[0].AsObject()
if err != nil {
return bridge.JsException(ctx, "first argument must be an object")
}
input, _ := parseTraceInput(inputObj, ctx)
// Parse option
optionObj, err := args[1].AsObject()
if err != nil {
return bridge.JsException(ctx, "second argument must be an object")
}
option := parseTraceNodeOption(optionObj)
// Call node.Add
childNode, err := node.Add(input, option)
if err != nil {
return bridge.JsException(ctx, err.Error())
}
// Create child node JS object
childNodeObj, err := NewNodeObject(ctx, childNode)
if err != nil {
return bridge.JsException(ctx, err.Error())
}
return childNodeObj
})
}
func nodeParallelMethod(iso *v8go.Isolate, node types.Node) *v8go.FunctionTemplate {
return v8go.NewFunctionTemplate(iso, func(info *v8go.FunctionCallbackInfo) *v8go.Value {
ctx := info.Context()
args := info.Args()
if len(args) < 1 {
return bridge.JsException(ctx, "Parallel requires an array argument")
}
// Parse array
arrayObj, err := args[0].AsObject()
if err != nil {
return bridge.JsException(ctx, "argument must be an array")
}
lengthVal, err := arrayObj.Get("length")
if err != nil {
return bridge.JsException(ctx, "invalid array")
}
length := int(lengthVal.Int32())
parallelInputs := make([]types.TraceParallelInput, 0, length)
for i := 0; i < length; i++ {
itemVal, err := arrayObj.GetIdx(uint32(i))
if err != nil {
continue
}
itemObj, err := itemVal.AsObject()
if err != nil {
continue
}
// Parse input
var input types.TraceInput
if inputVal, err := itemObj.Get("input"); err == nil && inputVal.IsObject() {
inputObjInner, _ := inputVal.AsObject()
input, _ = parseTraceInput(inputObjInner, ctx)
}
// Parse option
var option types.TraceNodeOption
if optionVal, err := itemObj.Get("option"); err == nil && optionVal.IsObject() {
optionObjInner, _ := optionVal.AsObject()
option = parseTraceNodeOption(optionObjInner)
}
parallelInputs = append(parallelInputs, types.TraceParallelInput{
Input: input,
Option: option,
})
}
// Call node.Parallel
childNodes, err := node.Parallel(parallelInputs)
if err != nil {
return bridge.JsException(ctx, err.Error())
}
// Create array of node objects
result := make([]interface{}, len(childNodes))
for i, childNode := range childNodes {
childNodeObj, err := NewNodeObject(ctx, childNode)
if err != nil {
continue
}
result[i] = childNodeObj
}
jsVal, err := bridge.JsValue(ctx, result)
if err != nil {
return bridge.JsException(ctx, err.Error())
}
return jsVal
})
}
func nodeSetOutputMethod(iso *v8go.Isolate, node types.Node) *v8go.FunctionTemplate {
return v8go.NewFunctionTemplate(iso, func(info *v8go.FunctionCallbackInfo) *v8go.Value {
ctx := info.Context()
args := info.Args()
if len(args) < 1 {
return bridge.JsException(ctx, "SetOutput requires an output argument")
}
output, err := bridge.GoValue(args[0], ctx)
if err != nil {
return bridge.JsException(ctx, err.Error())
}
if err := node.SetOutput(output); err != nil {
return bridge.JsException(ctx, err.Error())
}
return info.This().Value
})
}
func nodeSetMetadataMethod(iso *v8go.Isolate, node types.Node) *v8go.FunctionTemplate {
return v8go.NewFunctionTemplate(iso, func(info *v8go.FunctionCallbackInfo) *v8go.Value {
ctx := info.Context()
args := info.Args()
if len(args) < 2 {
return bridge.JsException(ctx, "SetMetadata requires 2 arguments: (key, value)")
}
key := args[0].String()
value, err := bridge.GoValue(args[1], ctx)
if err != nil {
return bridge.JsException(ctx, err.Error())
}
if err := node.SetMetadata(key, value); err != nil {
return bridge.JsException(ctx, err.Error())
}
return info.This().Value
})
}
func nodeCompleteMethod(iso *v8go.Isolate, node types.Node) *v8go.FunctionTemplate {
return v8go.NewFunctionTemplate(iso, func(info *v8go.FunctionCallbackInfo) *v8go.Value {
ctx := info.Context()
args := info.Args()
// Optional output parameter
if len(args) > 0 {
output, err := bridge.GoValue(args[0], ctx)
if err != nil {
return bridge.JsException(ctx, err.Error())
}
if err := node.Complete(output); err != nil {
return bridge.JsException(ctx, err.Error())
}
} else {
if err := node.Complete(); err != nil {
return bridge.JsException(ctx, err.Error())
}
}
return info.This().Value
})
}
func nodeFailMethod(iso *v8go.Isolate, node types.Node) *v8go.FunctionTemplate {
return v8go.NewFunctionTemplate(iso, func(info *v8go.FunctionCallbackInfo) *v8go.Value {
ctx := info.Context()
args := info.Args()
if len(args) < 1 {
return bridge.JsException(ctx, "Fail requires an error message")
}
errMsg := args[0].String()
if err := node.Fail(fmt.Errorf("%s", errMsg)); err != nil {
return bridge.JsException(ctx, err.Error())
}
return info.This().Value
})
}