Compare commits

...

3 Commits

Author SHA1 Message Date
AI Engineer
f25c7c3f92 feat(file): 具名化 JS 导出并动态包裹错误(by AI) 2026-06-21 10:15:18 +08:00
AI Engineer
121e90390b chore: align dependencies to v1.5.4 (by AI) 2026-06-11 20:14:20 +08:00
AI Engineer
03aeb5627e chore: align infrastructure to v1.5.4 (by AI) 2026-06-11 18:59:25 +08:00
4 changed files with 280 additions and 153 deletions

View File

@ -1,5 +1,15 @@
# Changelog # 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。
## v1.5.3 (2026-06-09)
- **版本对齐**: 基础设施全局对齐 v1.5.3。
## v1.5.2 (2026-06-08) ## v1.5.2 (2026-06-08)
- **JS 对齐**: 将所有注册到 `jsmod` 的导出方法名统一为 PascalCase`Read`, `Write`, `GetFileInfo`),消除 JS 环境下的调用摩擦。 - **JS 对齐**: 将所有注册到 `jsmod` 的导出方法名统一为 PascalCase`Read`, `Write`, `GetFileInfo`),消除 JS 环境下的调用摩擦。

View File

@ -1,7 +1,7 @@
# Test Report: @go/file # Test Report: @go/file
## 📋 测试概览 ## 📋 测试概览
- **测试时间**: 2026-05-06 - **测试时间**: 2026-06-21
- **测试环境**: darwin/amd64 - **测试环境**: darwin/amd64
- **Go 版本**: 1.25.0 - **Go 版本**: 1.25.0
@ -17,9 +17,10 @@
## 🛡️ 鲁棒性防御 (Robustness) ## 🛡️ 鲁棒性防御 (Robustness)
- **摩擦消除**:移除 `Must` 系列 API极大降低了业务逻辑中的错误处理心智负担。 - **摩擦消除**:移除 `Must` 系列 API极大降低了业务逻辑中的错误处理心智负担。
- **内存虚拟化**:支持透明的内存文件系统操作,与磁盘文件 API 完全一致。 - **内存虚拟化**:支持透明的内存文件系统操作,与磁盘文件 API 完全一致。
- **JS 错误调用栈**JS 桥接层改用具名导出并使用 `jsmod.MakeError` 包裹错误,确保 JS 抛出异常时携带准确的 Go 运行时堆栈。
## ⚡ 性能基准 (Benchmarks) ## ⚡ 性能基准 (Benchmarks)
| 函数 | 平均耗时 | 性能分析 | | 函数 | 平均耗时 | 性能分析 |
| :--- | :--- | :--- | | :--- | :--- | :--- |
| `MarshalFile` | **115787 ns/op** | 包含 IO 写入的高性能序列化。 | | `MarshalFile` | **104568 ns/op** | 包含 IO 写入的高性能序列化。 |
| `UnmarshalFile` | **24682 ns/op** | 高效的反序列化。 | | `UnmarshalFile` | **23580 ns/op** | 高效的反序列化。 |

10
go.mod
View File

@ -3,15 +3,15 @@ module apigo.cc/go/file
go 1.25.0 go 1.25.0
require ( require (
apigo.cc/go/cast v1.5.0 apigo.cc/go/cast v1.5.3
apigo.cc/go/encoding v1.5.0 apigo.cc/go/encoding v1.5.4
apigo.cc/go/jsmod v1.5.0 apigo.cc/go/jsmod v1.5.3
apigo.cc/go/safe v1.5.0 apigo.cc/go/safe v1.5.2
gopkg.in/yaml.v3 v3.0.1 gopkg.in/yaml.v3 v3.0.1
) )
require ( require (
apigo.cc/go/rand v1.5.0 // indirect apigo.cc/go/rand v1.5.3
github.com/kr/pretty v0.3.0 // indirect github.com/kr/pretty v0.3.0 // indirect
github.com/rogpeppe/go-internal v1.14.1 // indirect github.com/rogpeppe/go-internal v1.14.1 // indirect
golang.org/x/crypto v0.52.0 // indirect golang.org/x/crypto v0.52.0 // indirect

View File

@ -9,157 +9,273 @@ import (
func init() { func init() {
jsmod.Register("file", map[string]any{ jsmod.Register("file", map[string]any{
// 读操作 (映射到私有包装器) // 读操作 (映射到私有包装器)
"Exists": func(ctx context.Context, path string) bool { "Exists": jsExists,
"Read": jsRead,
"ReadBytes": jsReadBytes,
"ReadLines": jsReadLines,
"ReadDir": jsReadDir,
"GetFileInfo": jsGetFileInfo,
// 写操作
"Write": jsWrite,
"WriteBytes": jsWriteBytes,
"Remove": jsRemove,
"Mkdir": jsMkdir,
"Copy": jsCopy,
"Move": jsMove,
"Replace": jsReplace,
// 序列化
"UnmarshalFile": jsUnmarshalFile,
"MarshalFile": jsMarshalFile,
"MarshalFilePretty": jsMarshalFilePretty,
// 归档
"Archive": jsArchive,
"Extract": jsExtract,
// 压缩工具
"Compress": jsCompress,
"Decompress": jsDecompress,
})
}
func jsExists(ctx context.Context, path string) bool {
p, err := VerifyPathForSafeMode(ctx, path) p, err := VerifyPathForSafeMode(ctx, path)
if err != nil { if err != nil {
return false return false
} }
return Exists(p) return Exists(p)
}, }
"Read": func(ctx context.Context, path string) (string, error) {
func jsRead(ctx context.Context, path string) (string, error) {
p, err := VerifyPathForSafeMode(ctx, path) p, err := VerifyPathForSafeMode(ctx, path)
if err != nil { if err != nil {
return "", err return "", jsmod.MakeError(err)
} }
return Read(p) res, err := Read(p)
}, if err != nil {
"ReadBytes": func(ctx context.Context, path string) ([]byte, error) { return "", jsmod.MakeError(err)
}
return res, nil
}
func jsReadBytes(ctx context.Context, path string) ([]byte, error) {
p, err := VerifyPathForSafeMode(ctx, path) p, err := VerifyPathForSafeMode(ctx, path)
if err != nil { if err != nil {
return nil, err return nil, jsmod.MakeError(err)
} }
return ReadBytes(p) res, err := ReadBytes(p)
}, if err != nil {
"ReadLines": func(ctx context.Context, path string) ([]string, error) { return nil, jsmod.MakeError(err)
}
return res, nil
}
func jsReadLines(ctx context.Context, path string) ([]string, error) {
p, err := VerifyPathForSafeMode(ctx, path) p, err := VerifyPathForSafeMode(ctx, path)
if err != nil { if err != nil {
return nil, err return nil, jsmod.MakeError(err)
} }
return ReadLines(p) res, err := ReadLines(p)
}, if err != nil {
"ReadDir": func(ctx context.Context, path string) ([]FileInfo, error) { return nil, jsmod.MakeError(err)
}
return res, nil
}
func jsReadDir(ctx context.Context, path string) ([]FileInfo, error) {
p, err := VerifyPathForSafeMode(ctx, path) p, err := VerifyPathForSafeMode(ctx, path)
if err != nil { if err != nil {
return nil, err return nil, jsmod.MakeError(err)
} }
return ReadDir(p) res, err := ReadDir(p)
}, if err != nil {
"GetFileInfo": func(ctx context.Context, path string) *FileInfo { return nil, jsmod.MakeError(err)
}
return res, nil
}
func jsGetFileInfo(ctx context.Context, path string) *FileInfo {
p, err := VerifyPathForSafeMode(ctx, path) p, err := VerifyPathForSafeMode(ctx, path)
if err != nil { if err != nil {
return nil return nil
} }
return GetFileInfo(p) return GetFileInfo(p)
}, }
// 写操作 func jsWrite(ctx context.Context, path string, content string) error {
"Write": func(ctx context.Context, path string, content string) error { p, err := VerifyPathForSafeMode(ctx, path)
p, err := VerifyPathForSafeMode(ctx, path) if err != nil {
if err != nil { return jsmod.MakeError(err)
return err }
} err = Write(p, content)
return Write(p, content) if err != nil {
}, return jsmod.MakeError(err)
"WriteBytes": func(ctx context.Context, path string, content []byte) error { }
p, err := VerifyPathForSafeMode(ctx, path) return nil
if err != nil { }
return err
} func jsWriteBytes(ctx context.Context, path string, content []byte) error {
return WriteBytes(p, content) p, err := VerifyPathForSafeMode(ctx, path)
}, if err != nil {
"Remove": func(ctx context.Context, path string) error { return jsmod.MakeError(err)
p, err := VerifyPathForSafeMode(ctx, path) }
if err != nil { err = WriteBytes(p, content)
return err if err != nil {
} return jsmod.MakeError(err)
return Remove(p) }
}, return nil
"Mkdir": func(ctx context.Context, path string) error { }
p, err := VerifyPathForSafeMode(ctx, path)
if err != nil { func jsRemove(ctx context.Context, path string) error {
return err p, err := VerifyPathForSafeMode(ctx, path)
} if err != nil {
return Mkdir(p) return jsmod.MakeError(err)
}, }
"Copy": func(ctx context.Context, from, to string) error { err = Remove(p)
pFrom, err := VerifyPathForSafeMode(ctx, from) if err != nil {
if err != nil { return jsmod.MakeError(err)
return err }
} return nil
pTo, err := VerifyPathForSafeMode(ctx, to) }
if err != nil {
return err func jsMkdir(ctx context.Context, path string) error {
} p, err := VerifyPathForSafeMode(ctx, path)
return Copy(pFrom, pTo) if err != nil {
}, return jsmod.MakeError(err)
"Move": func(ctx context.Context, from, to string) error { }
pFrom, err := VerifyPathForSafeMode(ctx, from) err = Mkdir(p)
if err != nil { if err != nil {
return err return jsmod.MakeError(err)
} }
pTo, err := VerifyPathForSafeMode(ctx, to) return nil
if err != nil { }
return err
} func jsCopy(ctx context.Context, from, to string) error {
return Move(pFrom, pTo) pFrom, err := VerifyPathForSafeMode(ctx, from)
}, if err != nil {
"Replace": func(ctx context.Context, path, old, new string) error { return jsmod.MakeError(err)
p, err := VerifyPathForSafeMode(ctx, path) }
if err != nil { pTo, err := VerifyPathForSafeMode(ctx, to)
return err if err != nil {
} return jsmod.MakeError(err)
return Replace(p, old, new) }
}, err = Copy(pFrom, pTo)
if err != nil {
// 序列化 return jsmod.MakeError(err)
"UnmarshalFile": func(ctx context.Context, path string, to any) error { }
p, err := VerifyPathForSafeMode(ctx, path) return nil
if err != nil { }
return err
} func jsMove(ctx context.Context, from, to string) error {
return UnmarshalFile(p, to) pFrom, err := VerifyPathForSafeMode(ctx, from)
}, if err != nil {
"MarshalFile": func(ctx context.Context, path string, data any) error { return jsmod.MakeError(err)
p, err := VerifyPathForSafeMode(ctx, path) }
if err != nil { pTo, err := VerifyPathForSafeMode(ctx, to)
return err if err != nil {
} return jsmod.MakeError(err)
return MarshalFile(p, data) }
}, err = Move(pFrom, pTo)
"MarshalFilePretty": func(ctx context.Context, path string, data any) error { if err != nil {
p, err := VerifyPathForSafeMode(ctx, path) return jsmod.MakeError(err)
if err != nil { }
return err return nil
} }
return MarshalFilePretty(p, data)
}, func jsReplace(ctx context.Context, path, old, new string) error {
p, err := VerifyPathForSafeMode(ctx, path)
// 归档 if err != nil {
"Archive": func(ctx context.Context, src, dest string) error { return jsmod.MakeError(err)
pSrc, err := VerifyPathForSafeMode(ctx, src) }
if err != nil { err = Replace(p, old, new)
return err if err != nil {
} return jsmod.MakeError(err)
pDest, err := VerifyPathForSafeMode(ctx, dest) }
if err != nil { return nil
return err }
}
return Archive(pSrc, pDest) func jsUnmarshalFile(ctx context.Context, path string, to any) error {
}, p, err := VerifyPathForSafeMode(ctx, path)
"Extract": func(ctx context.Context, src, dest string, strip bool) error { if err != nil {
pSrc, err := VerifyPathForSafeMode(ctx, src) return jsmod.MakeError(err)
if err != nil { }
return err err = UnmarshalFile(p, to)
} if err != nil {
pDest, err := VerifyPathForSafeMode(ctx, dest) return jsmod.MakeError(err)
if err != nil { }
return err return nil
} }
return Extract(pSrc, pDest, strip)
}, func jsMarshalFile(ctx context.Context, path string, data any) error {
p, err := VerifyPathForSafeMode(ctx, path)
// 压缩工具 (无路径,不校验) if err != nil {
"Compress": Compress, return jsmod.MakeError(err)
"Decompress": Decompress, }
}) 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
} }