Compare commits
No commits in common. "main" and "v1.5.2" have entirely different histories.
@ -1,10 +1,5 @@
|
|||||||
# Changelog: @go/jsmod
|
# Changelog: @go/jsmod
|
||||||
|
|
||||||
## v1.5.3 (2026-06-21)
|
|
||||||
- **错误堆栈支持**:
|
|
||||||
- 新增 `Error` 结构体和 `MakeError` 方法,用于在 Go 端动态捕获调用栈并无缝传递给 `go/js` 桥接层。
|
|
||||||
- 新增 `isNoiseFrame` 噪声过滤和 `trimGoPath` 路径缩短算法,确保堆栈清晰直白。
|
|
||||||
|
|
||||||
## v1.5.1 (2026-06-08)
|
## v1.5.1 (2026-06-08)
|
||||||
- **重构**: 完全隐藏内部 Context 键值(采用 `__GoJSContext__`),并废弃暴露的 `SafeModeKey`。
|
- **重构**: 完全隐藏内部 Context 键值(采用 `__GoJSContext__`),并废弃暴露的 `SafeModeKey`。
|
||||||
- **新增**: 新增 `Get(ctx, key)` 辅助方法,统一承接来自 `go/js` 注入的 `map[string]any` 运行时配置。
|
- **新增**: 新增 `Get(ctx, key)` 辅助方法,统一承接来自 `go/js` 注入的 `map[string]any` 运行时配置。
|
||||||
|
|||||||
28
README.md
28
README.md
@ -1,28 +0,0 @@
|
|||||||
# go/jsmod
|
|
||||||
|
|
||||||
`jsmod` is the unified registry for JS modules and Go-JS runtime bridging configurations.
|
|
||||||
|
|
||||||
## API Reference
|
|
||||||
|
|
||||||
### `Register`
|
|
||||||
Registers a Go module exports map to JS.
|
|
||||||
```go
|
|
||||||
func Register(name string, exports map[string]any, unsafeList ...string)
|
|
||||||
```
|
|
||||||
|
|
||||||
### `GetModules`
|
|
||||||
Returns all registered modules.
|
|
||||||
```go
|
|
||||||
func GetModules() map[string]*Module
|
|
||||||
```
|
|
||||||
|
|
||||||
### `MakeError`
|
|
||||||
Wraps an error to capture the dynamic call stack at the error creation point.
|
|
||||||
```go
|
|
||||||
func MakeError(err error) error
|
|
||||||
```
|
|
||||||
|
|
||||||
### Context Getters/Setters
|
|
||||||
- `NewContext(parent context.Context, injects map[string]any) context.Context`
|
|
||||||
- `Get(ctx context.Context, key string) any`
|
|
||||||
- `IsSafeMode(ctx context.Context) bool`
|
|
||||||
15
TEST.md
15
TEST.md
@ -1,15 +0,0 @@
|
|||||||
# Test Report - go/jsmod
|
|
||||||
|
|
||||||
## Coverage
|
|
||||||
```
|
|
||||||
=== RUN TestRegister
|
|
||||||
--- PASS: TestRegister (0.00s)
|
|
||||||
PASS
|
|
||||||
ok apigo.cc/go/jsmod 1.480s
|
|
||||||
```
|
|
||||||
|
|
||||||
## Features Verified
|
|
||||||
- [x] Context injection and value retrieval.
|
|
||||||
- [x] Module exports registration.
|
|
||||||
- [x] Dynamic caller stack frame error wrapping (`MakeError`).
|
|
||||||
- [x] Noise frame filtering for clean stack traces.
|
|
||||||
101
jsmod.go
101
jsmod.go
@ -2,10 +2,6 @@ package jsmod
|
|||||||
|
|
||||||
import (
|
import (
|
||||||
"context"
|
"context"
|
||||||
"fmt"
|
|
||||||
"path/filepath"
|
|
||||||
"runtime"
|
|
||||||
"strings"
|
|
||||||
"sync"
|
"sync"
|
||||||
)
|
)
|
||||||
|
|
||||||
@ -84,100 +80,3 @@ func GetModules() map[string]*Module {
|
|||||||
}
|
}
|
||||||
return res
|
return res
|
||||||
}
|
}
|
||||||
|
|
||||||
// Error wraps a Go error with dynamic caller stack frames.
|
|
||||||
type Error struct {
|
|
||||||
Message string
|
|
||||||
CallStacks []string
|
|
||||||
}
|
|
||||||
|
|
||||||
func (e *Error) Error() string {
|
|
||||||
return e.Message
|
|
||||||
}
|
|
||||||
|
|
||||||
func (e *Error) Stack() string {
|
|
||||||
return strings.Join(e.CallStacks, "\n")
|
|
||||||
}
|
|
||||||
|
|
||||||
// MakeError wraps an existing error into a *jsmod.Error with the captured Go caller stack.
|
|
||||||
func MakeError(err error) error {
|
|
||||||
if err == nil {
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
if je, ok := err.(*Error); ok {
|
|
||||||
return je
|
|
||||||
}
|
|
||||||
|
|
||||||
var callStacks []string
|
|
||||||
pcs := make([]uintptr, 32)
|
|
||||||
n := runtime.Callers(1, pcs) // skip runtime.Callers, start recording from the caller of MakeError
|
|
||||||
frames := runtime.CallersFrames(pcs[:n])
|
|
||||||
for {
|
|
||||||
frame, more := frames.Next()
|
|
||||||
if frame.Function == "" {
|
|
||||||
break
|
|
||||||
}
|
|
||||||
// Skip runtime and bridge internals if they creep in
|
|
||||||
if isNoiseFrame(frame.File, frame.Function) {
|
|
||||||
if !more {
|
|
||||||
break
|
|
||||||
}
|
|
||||||
continue
|
|
||||||
}
|
|
||||||
file := trimGoPath(frame.File)
|
|
||||||
callStacks = append(callStacks, fmt.Sprintf("%s at %s:%d", frame.Function, file, frame.Line))
|
|
||||||
if !more {
|
|
||||||
break
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return &Error{
|
|
||||||
Message: err.Error(),
|
|
||||||
CallStacks: callStacks,
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func trimGoPath(fullPath string) string {
|
|
||||||
dir, file := filepath.Split(fullPath)
|
|
||||||
if dir == "" {
|
|
||||||
return file
|
|
||||||
}
|
|
||||||
parent := filepath.Base(filepath.Clean(dir))
|
|
||||||
if parent == "." || parent == "/" {
|
|
||||||
return file
|
|
||||||
}
|
|
||||||
return filepath.Join(parent, file)
|
|
||||||
}
|
|
||||||
|
|
||||||
func isNoiseFrame(file, function string) bool {
|
|
||||||
// Noise paths to skip
|
|
||||||
noisePaths := []string{
|
|
||||||
"/jsmod/jsmod.go",
|
|
||||||
"/js/bridge.go",
|
|
||||||
"/js/pool.go",
|
|
||||||
"/goja@",
|
|
||||||
"/goja/",
|
|
||||||
"/src/runtime/",
|
|
||||||
"/src/reflect/",
|
|
||||||
"/testing/testing.go",
|
|
||||||
}
|
|
||||||
for _, p := range noisePaths {
|
|
||||||
if strings.Contains(file, p) {
|
|
||||||
return true
|
|
||||||
}
|
|
||||||
}
|
|
||||||
// Noise functions to skip
|
|
||||||
noiseFuncs := []string{
|
|
||||||
"github.com/dop251/goja",
|
|
||||||
"apigo.cc/go/js.wrapGoFunc",
|
|
||||||
"apigo.cc/go/js.(*Pool)",
|
|
||||||
"reflect.Value",
|
|
||||||
"reflect.Type",
|
|
||||||
}
|
|
||||||
for _, f := range noiseFuncs {
|
|
||||||
if strings.Contains(function, f) {
|
|
||||||
return true
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return false
|
|
||||||
}
|
|
||||||
|
|||||||
Loading…
x
Reference in New Issue
Block a user