gojs

A high-performance JavaScript runtime library based on goja, designed for Go applications. Features script caching, concurrent execution, and rich built-in libraries.
一个基于 goja 的高性能 JavaScript 运行时库,专为 Go 应用程序设计。支持脚本缓存、并发执行、丰富的内置库。
English | 中文
Features
- High Performance: Based on goja engine with script pre-compilation and caching support
- Concurrent Safe: Supports multi-goroutine concurrent script execution
- Rich Built-in Libraries: Integrated lodash, moment.js, CryptoJS, xml-js, formulajs, etc.
- Buffer Extensions: Supports BigInt64/BigUInt64 read/write operations
- CRC Checksum: Supports CRC16/CRC32/CRC64/Modbus CRC checksums
- Compression: Supports gzip and zip decompression
- Encoding Conversion: Supports iconv encoding conversion
- Logging Integration: Supports structured logging output
Installation
go get github.com/felix-186/gojs
Quick Start
Basic Usage
package main
import (
"fmt"
"github.com/felix-186/gojs"
)
func main() {
script := `
function handler(input) {
return _.map(input, function(x) { return x * 2; });
}
`
result, err := gojs.Run(script, []interface{}{1, 2, 3, 4, 5}...)
if err != nil {
panic(err)
}
fmt.Println(result.Export()) // [2, 4, 6, 8, 10]
}
Using Cache
// Cache script with ID
result, err := gojs.RunByIdAndScript("my-script-id", script, args...)
// Execute cached script
_, err := gojs.GetJsVm("my-script", script)
result, err := gojs.RunById("my-script", args...)
API Reference
Core Functions
| Function |
Description |
Run(script string, values ...interface{}) |
Execute JavaScript script |
RunByIdAndScript(id, script string, values ...interface{}) |
Execute with ID caching |
RunById(id string, values ...interface{}) |
Execute cached script |
GetJsVm(id, script string) |
Get or create VM instance |
GetVm() |
Create new VM instance |
GetVmCallback(cb Callback) |
Create VM with custom configuration |
Buffer Operations
| Function |
Description |
BufferToBytes(bufferVal goja.Value) |
Convert JS Buffer to Go bytes |
BytesToBuffer(vm *goja.Runtime, bs []byte) |
Convert Go bytes to JS Buffer |
Utility Functions
| Function |
Description |
IsValid(value goja.Value) |
Check if value is valid (not nil/undefined/NaN/null) |
IsBuffer(value goja.Value) |
Check if value is a Buffer |
Built-in Libraries
| Library |
Global Variable |
Description |
| lodash |
_ |
Utility library |
| moment.js |
moment |
Date/time handling |
| CryptoJS |
CryptoJS |
Encryption library |
| xml-js |
xmlJs |
XML to JSON conversion |
| formulajs |
formulajs |
Excel formula library |
| iconv-lite |
iconv |
Encoding conversion |
| pako |
pako |
zlib compression |
| forge |
forge |
Crypto utilities |
Buffer Extensions
// BigInt64 read/write
buffer.writeBigInt64LE(value, offset);
buffer.writeBigInt64BE(value, offset);
buffer.readBigInt64LE(offset);
buffer.readBigInt64BE(offset);
// BigUInt64 read/write
buffer.writeBigUInt64LE(value, offset);
buffer.writeBigUInt64BE(value, offset);
buffer.readBigUInt64LE(offset);
buffer.readBigUInt64BE(offset);
CRC Checksum
crc.checksum16(buffer, poly); // CRC16
crc.checksum32(buffer, poly); // CRC32
crc.checksum64(buffer, poly); // CRC64
crc.checksumModbus(buffer); // Modbus CRC
API Library
apilib.SleepMill(1000); // Sleep (milliseconds)
apilib.UnGzip(gzipData); // Decompress gzip
apilib.Unzip(zipData); // Decompress zip
Logging
logger.Debug("debug message", 1);
logger.Info("info message", 2);
logger.Warn("warn message", 3);
logger.Error("error message", 4);
Examples
Data Processing
script := `
function handler(data) {
return _.map(data, function(item) {
return {
id: item.id,
value: item.value * 2,
timestamp: moment().format('YYYY-MM-DD HH:mm:ss')
};
});
}
`
data := []map[string]interface{}{
{"id": "1", "value": 10},
{"id": "2", "value": 20},
}
result, err := gojs.Run(script, data)
Encryption
script := `
function handler(text) {
var encrypted = CryptoJS.AES.encrypt(text, "secret-key").toString();
var decrypted = CryptoJS.AES.decrypt(encrypted, "secret-key").toString(CryptoJS.enc.Utf8);
return { encrypted: encrypted, decrypted: decrypted };
}
`
XML Conversion
script := `
function handler(xmlString) {
return xmlJs.xml2json(xmlString, { compact: true });
}
`
Script Caching
// First execution compiles and caches
gojs.RunByIdAndScript("script-1", script, data...)
// Subsequent executions use cache
gojs.RunById("script-1", data...)
Concurrent Execution
var wg sync.WaitGroup
for i := 0; i < 100; i++ {
wg.Add(1)
go func(idx int) {
defer wg.Done()
result, _ := gojs.RunById("script-1", idx)
}(i)
}
wg.Wait()
Error Codes
| Code |
Description |
| 100040001 |
Failed to set object in VM |
| 100040002 |
Failed to run pre-compiled program |
| 100040003 |
Failed to run script |
| 100040004 |
Handler function not found |
| 100040005 |
Failed to call handler function |
| 100040006 |
VM not found |
| 100040007 |
Parameter is not Object |
| 100040008 |
Buffer not found |
| 100040009 |
Parameter is not ArrayBuffer |
| 100040010 |
Offset or length exceeds buffer size |
| 100040011 |
Buffer not loaded |
| 100040012 |
Buffer.from method not loaded |
| 100040013 |
Buffer.from is not FunctionCall |
Testing
# Run all tests
go test ./...
# Run with coverage
go test -cover ./...
# Generate coverage report
go test -coverprofile=coverage.out ./...
go tool cover -html=coverage.out
# Run benchmarks
go test -bench=. ./...
Dependencies
Contributing
Contributions are welcome! Please feel free to submit a Pull Request.
License
MIT License
特性
- 高性能: 基于 goja 引擎,支持脚本预编译和缓存
- 并发安全: 支持多 goroutine 并发执行脚本
- 内置库丰富: 集成 lodash、moment.js、CryptoJS、xml-js、formulajs 等常用库
- Buffer 扩展: 支持 BigInt64/BigUInt64 读写操作
- CRC 校验: 支持 CRC16/CRC32/CRC64/Modbus CRC 校验
- 压缩解压: 支持 gzip、zip 格式的压缩解压
- 编码转换: 支持 iconv 编码转换
- 日志集成: 支持结构化日志输出
安装
go get github.com/felix-186/gojs
快速开始
基本使用
package main
import (
"fmt"
"github.com/felix-186/gojs"
)
func main() {
script := `
function handler(input) {
return _.map(input, function(x) { return x * 2; });
}
`
result, err := gojs.Run(script, []interface{}{1, 2, 3, 4, 5}...)
if err != nil {
panic(err)
}
fmt.Println(result.Export()) // [2, 4, 6, 8, 10]
}
使用缓存执行
// 使用 ID 缓存脚本
result, err := gojs.RunByIdAndScript("my-script-id", script, args...)
// 使用已有缓存执行
_, err := gojs.GetJsVm("my-script", script)
result, err := gojs.RunById("my-script", args...)
API 文档
核心函数
| 函数 |
说明 |
Run(script string, values ...interface{}) |
执行 JavaScript 脚本 |
RunByIdAndScript(id, script string, values ...interface{}) |
使用指定 ID 执行脚本,支持缓存 |
RunById(id string, values ...interface{}) |
使用已缓存的脚本执行 |
GetJsVm(id, script string) |
获取或创建 JavaScript 虚拟机实例 |
GetVm() |
创建新的 JavaScript 虚拟机实例 |
GetVmCallback(cb Callback) |
创建虚拟机并执行回调函数进行自定义配置 |
Buffer 操作
| 函数 |
说明 |
BufferToBytes(bufferVal goja.Value) |
将 JavaScript Buffer 对象转换为 Go 字节切片 |
BytesToBuffer(vm *goja.Runtime, bs []byte) |
将 Go 字节切片转换为 JavaScript Buffer 对象 |
工具函数
| 函数 |
说明 |
IsValid(value goja.Value) |
判断 JavaScript 值是否有效(非 nil、undefined、NaN、null) |
IsBuffer(value goja.Value) |
判断值是否为 Buffer 对象 |
内置库
| 库名 |
全局变量 |
说明 |
| lodash |
_ |
实用工具库 |
| moment.js |
moment |
日期时间处理 |
| CryptoJS |
CryptoJS |
加密库 |
| xml-js |
xmlJs |
XML 与 JSON 转换 |
| formulajs |
formulajs |
Excel 公式库 |
| iconv-lite |
iconv |
编码转换 |
| pako |
pako |
zlib 压缩 |
| forge |
forge |
加密工具库 |
Buffer 扩展方法
// BigInt64 读写
buffer.writeBigInt64LE(value, offset);
buffer.writeBigInt64BE(value, offset);
buffer.readBigInt64LE(offset);
buffer.readBigInt64BE(offset);
// BigUInt64 读写
buffer.writeBigUInt64LE(value, offset);
buffer.writeBigUInt64BE(value, offset);
buffer.readBigUInt64LE(offset);
buffer.readBigUInt64BE(offset);
CRC 校验
crc.checksum16(buffer, poly); // CRC16
crc.checksum32(buffer, poly); // CRC32
crc.checksum64(buffer, poly); // CRC64
crc.checksumModbus(buffer); // Modbus CRC
API 库
apilib.SleepMill(1000); // 休眠(毫秒)
apilib.UnGzip(gzipData); // 解压 gzip
apilib.Unzip(zipData); // 解压 zip
日志
logger.Debug("debug message", 1);
logger.Info("info message", 2);
logger.Warn("warn message", 3);
logger.Error("error message", 4);
示例
数据处理
script := `
function handler(data) {
return _.map(data, function(item) {
return {
id: item.id,
value: item.value * 2,
timestamp: moment().format('YYYY-MM-DD HH:mm:ss')
};
});
}
`
data := []map[string]interface{}{
{"id": "1", "value": 10},
{"id": "2", "value": 20},
}
result, err := gojs.Run(script, data)
加密解密
script := `
function handler(text) {
var encrypted = CryptoJS.AES.encrypt(text, "secret-key").toString();
var decrypted = CryptoJS.AES.decrypt(encrypted, "secret-key").toString(CryptoJS.enc.Utf8);
return { encrypted: encrypted, decrypted: decrypted };
}
`
XML 转换
script := `
function handler(xmlString) {
return xmlJs.xml2json(xmlString, { compact: true });
}
`
性能优化
脚本缓存
// 首次执行会编译并缓存
gojs.RunByIdAndScript("script-1", script, data...)
// 后续执行直接使用缓存
gojs.RunById("script-1", data...)
并发执行
var wg sync.WaitGroup
for i := 0; i < 100; i++ {
wg.Add(1)
go func(idx int) {
defer wg.Done()
result, _ := gojs.RunById("script-1", idx)
}(i)
}
wg.Wait()
错误码
| 错误码 |
说明 |
| 100040001 |
VM 设置对象失败 |
| 100040002 |
运行预编译程序失败 |
| 100040003 |
运行脚本失败 |
| 100040004 |
脚本函数 handler 未找到 |
| 100040005 |
调用 handler 函数失败 |
| 100040006 |
未找到 VM |
| 100040007 |
参数格式非 Object |
| 100040008 |
未找到 buffer |
| 100040009 |
参数格式非 arrayBuffer |
| 100040010 |
offset 或 length 超出 buffer 大小 |
| 100040011 |
未加载 Buffer |
| 100040012 |
未加载 Buffer from 方法 |
| 100040013 |
Buffer from 方法类型非 FunctionCall |
测试
# 运行所有测试
go test ./...
# 运行测试并显示覆盖率
go test -cover ./...
# 生成覆盖率报告
go test -coverprofile=coverage.out ./...
go tool cover -html=coverage.out
# 运行性能测试
go test -bench=. ./...
依赖
贡献
欢迎提交 Pull Request 参与贡献!
许可证
MIT License