package jscriptx import ( "log/slog" "time" ) // 引擎的配置项都在这个文件里,一处看全 New 能配什么。 // // 另有两组独立的选项:BundleOption(打包源码,见 bundle.go)由 WithBundleOptions // 带进来;ScopeOption(每次调用的作用域,见 scope.go)跟着 ctx 走,不属于引擎配置。 // Option 是 New 的配置项。 type Option func(*Engine) // WithGlobals 追加暴露给脚本的全局对象白名单。可以多次调用,同名后者覆盖前者。 // // value 为 map[string]any 时会被注入成一个只读的 JS 对象(逐层递归), // 脚本改不动它,多个 VM 之间也不会共享同一个可变的 Go map。 func WithGlobals(globals map[string]any) Option { return func(e *Engine) { for k, v := range globals { e.globals[k] = v } } } // WithGlobal 暴露单个全局对象。 func WithGlobal(name string, value any) Option { return func(e *Engine) { e.globals[name] = value } } // WithTimeout 设置单次调用的时限,超时后脚本会被强制中断,调用方拿到 KindTimeout 错误。 // 传 0 表示不限时——只在明确知道脚本可信时才这么做。 func WithTimeout(d time.Duration) Option { return func(e *Engine) { e.timeout = d } } // WithMaxVMs 设置每个脚本最多缓存多少个 VM 实例。这是缓存上限不是并发上限: // 并发超过它时会临时新建 VM,用完直接丢弃,不会阻塞调用。 func WithMaxVMs(n int) Option { return func(e *Engine) { e.maxVMs = n } } // WithMaxCallStackSize 设置脚本的最大调用栈深度,传 0 用 goja 默认值。 func WithMaxCallStackSize(n int) Option { return func(e *Engine) { e.maxStack = n } } // WithLogger 设置日志器,脚本里的 console.* 会打到这里,带上 script 字段。 // 传 nil 表示不注入 console。 func WithLogger(l *slog.Logger) Option { return func(e *Engine) { e.logger = l } } // WithLoader 设置脚本源码的来源,Engine.Script 会用它按名字取脚本。 // 本库只定义 Loader 接口,具体从文件、数据库还是配置中心读由调用方实现。 // // 可以给多个,它们叠成一层层的,**后面的盖前面的**——取脚本时从最后一层往前找, // 谁先有就用谁的。把"定制层"放最后,业务侧放一份同名脚本就能改写默认实现: // // base, _ := esm.NewLoader("app/src") // custom, _ := esm.NewLoader("custom/src") // 配置可以跟 base 完全不同 // // e, err := jscriptx.New(jscriptx.WithLoader(base, custom)) // custom 盖 base // // 每层是独立的 Loader,各有各的配置(入口规则、目标版本、node_modules 位置、 // 扩展模块),来源也可以不同——一层来自磁盘目录,另一层来自数据库都行。 // 多次调用 WithLoader 会继续往后叠,效果跟一次传多个一样。 // // 叠多层时各层的 Prepared 必须一致,否则 New 报错,原因见 Overlay。 func WithLoader(loaders ...Loader) Option { return func(e *Engine) { e.loaderLayers = append(e.loaderLayers, loaders...) } } // WithAutoReload 打开后,每次 Engine.Script 都会问一次 Loader 拿版本号, // 版本变了就重新编译并换掉旧的 VM 池——这是"改脚本不重启进程"的开关。 // 代价是每次取脚本都会调一次 Loader.Load,实现方自己保证这个调用足够轻。 func WithAutoReload(on bool) Option { return func(e *Engine) { e.autoReload = on } } // WithBundleOptions 配置 Compile 打包源码时的行为,比如 WithResolveDir。 func WithBundleOptions(opts ...BundleOption) Option { return func(e *Engine) { e.bundleOpts = append(e.bundleOpts, opts...) } }