From f25c7c3f92d6595bf550be5253cb2078529b931f Mon Sep 17 00:00:00 2001 From: AI Engineer Date: Sun, 21 Jun 2026 10:15:18 +0800 Subject: [PATCH] =?UTF-8?q?feat(file):=20=E5=85=B7=E5=90=8D=E5=8C=96=20JS?= =?UTF-8?q?=20=E5=AF=BC=E5=87=BA=E5=B9=B6=E5=8A=A8=E6=80=81=E5=8C=85?= =?UTF-8?q?=E8=A3=B9=E9=94=99=E8=AF=AF=EF=BC=88by=20AI=EF=BC=89?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- CHANGELOG.md | 4 + TEST.md | 7 +- go.mod | 10 +- js_export.go | 406 +++++++++++++++++++++++++++++++++------------------ 4 files changed, 274 insertions(+), 153 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 412abd3..a29208f 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,5 +1,9 @@ # Changelog +## v1.5.5 (2026-06-21) +- **JS 对齐**: 重构 JS 导出为具名函数,并引入 `jsmod.MakeError` 动态包装错误以获取调用栈。 +- **依赖更新**: 升级依赖 `jsmod` 至 `v1.5.3`,`cast` 至 `v1.5.3`,`rand` 至 `v1.5.3`,`encoding` 至 `v1.5.4`,`safe` 至 `v1.5.2`。 + ## v1.5.4 (2026-06-11) - **版本对齐**: 基础设施全局对齐 v1.5.4。 diff --git a/TEST.md b/TEST.md index 5afc9ed..cb996a4 100644 --- a/TEST.md +++ b/TEST.md @@ -1,7 +1,7 @@ # Test Report: @go/file ## 📋 测试概览 -- **测试时间**: 2026-05-06 +- **测试时间**: 2026-06-21 - **测试环境**: darwin/amd64 - **Go 版本**: 1.25.0 @@ -17,9 +17,10 @@ ## 🛡️ 鲁棒性防御 (Robustness) - **摩擦消除**:移除 `Must` 系列 API,极大降低了业务逻辑中的错误处理心智负担。 - **内存虚拟化**:支持透明的内存文件系统操作,与磁盘文件 API 完全一致。 +- **JS 错误调用栈**:JS 桥接层改用具名导出并使用 `jsmod.MakeError` 包裹错误,确保 JS 抛出异常时携带准确的 Go 运行时堆栈。 ## ⚡ 性能基准 (Benchmarks) | 函数 | 平均耗时 | 性能分析 | | :--- | :--- | :--- | -| `MarshalFile` | **115787 ns/op** | 包含 IO 写入的高性能序列化。 | -| `UnmarshalFile` | **24682 ns/op** | 高效的反序列化。 | +| `MarshalFile` | **104568 ns/op** | 包含 IO 写入的高性能序列化。 | +| `UnmarshalFile` | **23580 ns/op** | 高效的反序列化。 | diff --git a/go.mod b/go.mod index 0e3c947..e48760e 100644 --- a/go.mod +++ b/go.mod @@ -3,15 +3,15 @@ module apigo.cc/go/file go 1.25.0 require ( - apigo.cc/go/cast v1.5.2 - apigo.cc/go/encoding v1.5.3 - apigo.cc/go/jsmod v1.5.2 - apigo.cc/go/safe v1.5.1 + apigo.cc/go/cast v1.5.3 + apigo.cc/go/encoding v1.5.4 + apigo.cc/go/jsmod v1.5.3 + apigo.cc/go/safe v1.5.2 gopkg.in/yaml.v3 v3.0.1 ) require ( - apigo.cc/go/rand v1.5.2 + apigo.cc/go/rand v1.5.3 github.com/kr/pretty v0.3.0 // indirect github.com/rogpeppe/go-internal v1.14.1 // indirect golang.org/x/crypto v0.52.0 // indirect diff --git a/js_export.go b/js_export.go index c8bf16b..ce773c7 100644 --- a/js_export.go +++ b/js_export.go @@ -9,157 +9,273 @@ import ( func init() { jsmod.Register("file", map[string]any{ // 读操作 (映射到私有包装器) - "Exists": func(ctx context.Context, path string) bool { - p, err := VerifyPathForSafeMode(ctx, path) - if err != nil { - return false - } - return Exists(p) - }, - "Read": func(ctx context.Context, path string) (string, error) { - p, err := VerifyPathForSafeMode(ctx, path) - if err != nil { - return "", err - } - return Read(p) - }, - "ReadBytes": func(ctx context.Context, path string) ([]byte, error) { - p, err := VerifyPathForSafeMode(ctx, path) - if err != nil { - return nil, err - } - return ReadBytes(p) - }, - "ReadLines": func(ctx context.Context, path string) ([]string, error) { - p, err := VerifyPathForSafeMode(ctx, path) - if err != nil { - return nil, err - } - return ReadLines(p) - }, - "ReadDir": func(ctx context.Context, path string) ([]FileInfo, error) { - p, err := VerifyPathForSafeMode(ctx, path) - if err != nil { - return nil, err - } - return ReadDir(p) - }, - "GetFileInfo": func(ctx context.Context, path string) *FileInfo { - p, err := VerifyPathForSafeMode(ctx, path) - if err != nil { - return nil - } - return GetFileInfo(p) - }, + "Exists": jsExists, + "Read": jsRead, + "ReadBytes": jsReadBytes, + "ReadLines": jsReadLines, + "ReadDir": jsReadDir, + "GetFileInfo": jsGetFileInfo, // 写操作 - "Write": func(ctx context.Context, path string, content string) error { - p, err := VerifyPathForSafeMode(ctx, path) - if err != nil { - return err - } - return Write(p, content) - }, - "WriteBytes": func(ctx context.Context, path string, content []byte) error { - p, err := VerifyPathForSafeMode(ctx, path) - if err != nil { - return err - } - return WriteBytes(p, content) - }, - "Remove": func(ctx context.Context, path string) error { - p, err := VerifyPathForSafeMode(ctx, path) - if err != nil { - return err - } - return Remove(p) - }, - "Mkdir": func(ctx context.Context, path string) error { - p, err := VerifyPathForSafeMode(ctx, path) - if err != nil { - return err - } - return Mkdir(p) - }, - "Copy": func(ctx context.Context, from, to string) error { - pFrom, err := VerifyPathForSafeMode(ctx, from) - if err != nil { - return err - } - pTo, err := VerifyPathForSafeMode(ctx, to) - if err != nil { - return err - } - return Copy(pFrom, pTo) - }, - "Move": func(ctx context.Context, from, to string) error { - pFrom, err := VerifyPathForSafeMode(ctx, from) - if err != nil { - return err - } - pTo, err := VerifyPathForSafeMode(ctx, to) - if err != nil { - return err - } - return Move(pFrom, pTo) - }, - "Replace": func(ctx context.Context, path, old, new string) error { - p, err := VerifyPathForSafeMode(ctx, path) - if err != nil { - return err - } - return Replace(p, old, new) - }, + "Write": jsWrite, + "WriteBytes": jsWriteBytes, + "Remove": jsRemove, + "Mkdir": jsMkdir, + "Copy": jsCopy, + "Move": jsMove, + "Replace": jsReplace, // 序列化 - "UnmarshalFile": func(ctx context.Context, path string, to any) error { - p, err := VerifyPathForSafeMode(ctx, path) - if err != nil { - return err - } - return UnmarshalFile(p, to) - }, - "MarshalFile": func(ctx context.Context, path string, data any) error { - p, err := VerifyPathForSafeMode(ctx, path) - if err != nil { - return err - } - return MarshalFile(p, data) - }, - "MarshalFilePretty": func(ctx context.Context, path string, data any) error { - p, err := VerifyPathForSafeMode(ctx, path) - if err != nil { - return err - } - return MarshalFilePretty(p, data) - }, + "UnmarshalFile": jsUnmarshalFile, + "MarshalFile": jsMarshalFile, + "MarshalFilePretty": jsMarshalFilePretty, // 归档 - "Archive": func(ctx context.Context, src, dest string) error { - pSrc, err := VerifyPathForSafeMode(ctx, src) - if err != nil { - return err - } - pDest, err := VerifyPathForSafeMode(ctx, dest) - if err != nil { - return err - } - return Archive(pSrc, pDest) - }, - "Extract": func(ctx context.Context, src, dest string, strip bool) error { - pSrc, err := VerifyPathForSafeMode(ctx, src) - if err != nil { - return err - } - pDest, err := VerifyPathForSafeMode(ctx, dest) - if err != nil { - return err - } - return Extract(pSrc, pDest, strip) - }, + "Archive": jsArchive, + "Extract": jsExtract, - // 压缩工具 (无路径,不校验) - "Compress": Compress, - "Decompress": Decompress, + // 压缩工具 + "Compress": jsCompress, + "Decompress": jsDecompress, }) } + +func jsExists(ctx context.Context, path string) bool { + p, err := VerifyPathForSafeMode(ctx, path) + if err != nil { + return false + } + return Exists(p) +} + +func jsRead(ctx context.Context, path string) (string, error) { + p, err := VerifyPathForSafeMode(ctx, path) + if err != nil { + return "", jsmod.MakeError(err) + } + res, err := Read(p) + if err != nil { + return "", jsmod.MakeError(err) + } + return res, nil +} + +func jsReadBytes(ctx context.Context, path string) ([]byte, error) { + p, err := VerifyPathForSafeMode(ctx, path) + if err != nil { + return nil, jsmod.MakeError(err) + } + res, err := ReadBytes(p) + if err != nil { + return nil, jsmod.MakeError(err) + } + return res, nil +} + +func jsReadLines(ctx context.Context, path string) ([]string, error) { + p, err := VerifyPathForSafeMode(ctx, path) + if err != nil { + return nil, jsmod.MakeError(err) + } + res, err := ReadLines(p) + if err != nil { + return nil, jsmod.MakeError(err) + } + return res, nil +} + +func jsReadDir(ctx context.Context, path string) ([]FileInfo, error) { + p, err := VerifyPathForSafeMode(ctx, path) + if err != nil { + return nil, jsmod.MakeError(err) + } + res, err := ReadDir(p) + if err != nil { + return nil, jsmod.MakeError(err) + } + return res, nil +} + +func jsGetFileInfo(ctx context.Context, path string) *FileInfo { + p, err := VerifyPathForSafeMode(ctx, path) + if err != nil { + return nil + } + return GetFileInfo(p) +} + +func jsWrite(ctx context.Context, path string, content string) error { + p, err := VerifyPathForSafeMode(ctx, path) + if err != nil { + return jsmod.MakeError(err) + } + err = Write(p, content) + if err != nil { + return jsmod.MakeError(err) + } + return nil +} + +func jsWriteBytes(ctx context.Context, path string, content []byte) error { + p, err := VerifyPathForSafeMode(ctx, path) + if err != nil { + return jsmod.MakeError(err) + } + err = WriteBytes(p, content) + if err != nil { + return jsmod.MakeError(err) + } + return nil +} + +func jsRemove(ctx context.Context, path string) error { + p, err := VerifyPathForSafeMode(ctx, path) + if err != nil { + return jsmod.MakeError(err) + } + err = Remove(p) + if err != nil { + return jsmod.MakeError(err) + } + return nil +} + +func jsMkdir(ctx context.Context, path string) error { + p, err := VerifyPathForSafeMode(ctx, path) + if err != nil { + return jsmod.MakeError(err) + } + err = Mkdir(p) + if err != nil { + return jsmod.MakeError(err) + } + return nil +} + +func jsCopy(ctx context.Context, from, to string) error { + pFrom, err := VerifyPathForSafeMode(ctx, from) + if err != nil { + return jsmod.MakeError(err) + } + pTo, err := VerifyPathForSafeMode(ctx, to) + if err != nil { + return jsmod.MakeError(err) + } + err = Copy(pFrom, pTo) + if err != nil { + return jsmod.MakeError(err) + } + return nil +} + +func jsMove(ctx context.Context, from, to string) error { + pFrom, err := VerifyPathForSafeMode(ctx, from) + if err != nil { + return jsmod.MakeError(err) + } + pTo, err := VerifyPathForSafeMode(ctx, to) + if err != nil { + return jsmod.MakeError(err) + } + err = Move(pFrom, pTo) + if err != nil { + return jsmod.MakeError(err) + } + return nil +} + +func jsReplace(ctx context.Context, path, old, new string) error { + p, err := VerifyPathForSafeMode(ctx, path) + if err != nil { + return jsmod.MakeError(err) + } + err = Replace(p, old, new) + if err != nil { + return jsmod.MakeError(err) + } + return nil +} + +func jsUnmarshalFile(ctx context.Context, path string, to any) error { + p, err := VerifyPathForSafeMode(ctx, path) + if err != nil { + return jsmod.MakeError(err) + } + err = UnmarshalFile(p, to) + if err != nil { + return jsmod.MakeError(err) + } + return nil +} + +func jsMarshalFile(ctx context.Context, path string, data any) error { + p, err := VerifyPathForSafeMode(ctx, path) + if err != nil { + return jsmod.MakeError(err) + } + err = MarshalFile(p, data) + if err != nil { + return jsmod.MakeError(err) + } + return nil +} + +func jsMarshalFilePretty(ctx context.Context, path string, data any) error { + p, err := VerifyPathForSafeMode(ctx, path) + if err != nil { + return jsmod.MakeError(err) + } + err = MarshalFilePretty(p, data) + if err != nil { + return jsmod.MakeError(err) + } + return nil +} + +func jsArchive(ctx context.Context, src, dest string) error { + pSrc, err := VerifyPathForSafeMode(ctx, src) + if err != nil { + return jsmod.MakeError(err) + } + pDest, err := VerifyPathForSafeMode(ctx, dest) + if err != nil { + return jsmod.MakeError(err) + } + err = Archive(pSrc, pDest) + if err != nil { + return jsmod.MakeError(err) + } + return nil +} + +func jsExtract(ctx context.Context, src, dest string, strip bool) error { + pSrc, err := VerifyPathForSafeMode(ctx, src) + if err != nil { + return jsmod.MakeError(err) + } + pDest, err := VerifyPathForSafeMode(ctx, dest) + if err != nil { + return jsmod.MakeError(err) + } + err = Extract(pSrc, pDest, strip) + if err != nil { + return jsmod.MakeError(err) + } + return nil +} + +func jsCompress(data []byte, cType string) ([]byte, error) { + res, err := Compress(data, cType) + if err != nil { + return nil, jsmod.MakeError(err) + } + return res, nil +} + +func jsDecompress(data []byte, cType string) ([]byte, error) { + res, err := Decompress(data, cType) + if err != nil { + return nil, jsmod.MakeError(err) + } + return res, nil +}