// Package testext 给本库自己的测试提供一个**有状态的**扩展。 // // 本库只定义 Extension 接口,不带任何内置扩展——扩展该由用引擎的人按自己的 // 场景定义(框架侧的那套见 framework 的 internal/jscript/ext)。但测"作用域 // 共享状态""扩展改名""VM 复用后状态还在不在"这些行为,手上得有一个真的扩展, // 所以这里放一个最小的。 // // 它只为测试存在,不是给外部用的示范——真要写扩展看 Extension 接口的文档。 package testext import ( "sync" "git.fsdpf.net/go/jscriptx" ) // Store 是一份并发安全的键值表,同时也是一个 jscriptx 扩展。 type Store struct { name string mu sync.RWMutex data map[string]any } // New 造一个,全局名默认叫 store。 func New() *Store { return &Store{name: "store", data: map[string]any{}} } // Named 造一个用别的全局名的,用来测改名。 func Named(name string) *Store { return &Store{name: name, data: map[string]any{}} } func (s *Store) Name() string { return s.name } func (s *Store) Module() (string, string) { return "@jscriptx/store", typings } func (s *Store) Bindings() map[string]any { return map[string]any{ "Get": s.Get, "GetOr": s.GetOr, "Set": s.Set, "Incr": s.Incr, "Len": s.Len, } } // Get 取值,没有返回 nil(到脚本里是 null)。 func (s *Store) Get(key string) any { s.mu.RLock() defer s.mu.RUnlock() return s.data[key] } // GetOr 取值,没有返回 fallback。 func (s *Store) GetOr(key string, fallback any) any { s.mu.RLock() defer s.mu.RUnlock() if v, ok := s.data[key]; ok { return v } return fallback } // Set 写值。 func (s *Store) Set(key string, v any) { s.mu.Lock() defer s.mu.Unlock() s.data[key] = v } // Incr 累加,返回加完的值。键不存在按 0 算。 // // 用 float64 而不是整数:JS 的数字就是 float64,脚本写进来的值也是这个类型, // 用整数会让"Go 侧读回来"的断言在类型上对不上。 func (s *Store) Incr(key string, delta float64) float64 { s.mu.Lock() defer s.mu.Unlock() n, _ := s.data[key].(float64) n += delta s.data[key] = n return n } // Len 返回键的个数。 func (s *Store) Len() int { s.mu.RLock() defer s.mu.RUnlock() return len(s.data) } var _ jscriptx.Extension = (*Store)(nil) const typings = `export interface Store { Get(key: string): any GetOr(key: string, fallback: any): any Set(key: string, value: any): void Incr(key: string, delta: number): number Len(): number } declare const store: Store export default store `