package builtin import ( "fmt" "reflect" "strings" "sync" ) var ( builtinRegistry = make(map[string]Builtin) registryMutex sync.RWMutex collector *typeCollector ) func registerBuiltin[A Args, R any](name string, isAsync bool, fn Func[A, R]) { registryMutex.Lock() defer registryMutex.Unlock() if collector == nil { collector = newTypeCollector() } tType := reflect.TypeFor[A]() if tType.Kind() != reflect.Struct { panic(fmt.Sprintf("builtin %s: argument must be a struct type, got %v", name, tType)) } fnType := reflect.TypeOf(fn) types := collector.collectTypes(tType, fnType) paramTypes := collector.getParamTypes() builtinRegistry[name] = &builtinImpl[A, R]{ name: name, fn: fn, types: types, definition: generateTypeScriptDefinition(name, tType, fnType, isAsync, paramTypes), } } func GetBuiltinsDeclarations() string { registryMutex.RLock() defer registryMutex.RUnlock() typeDefinitions := make(map[string]bool) var typeDefs []string var functionDecls []string for _, builtin := range builtinRegistry { for _, t := range builtin.Types() { if !typeDefinitions[t] { typeDefinitions[t] = true typeDefs = append(typeDefs, t) } } functionDecls = append(functionDecls, builtin.Definition()) } result := strings.Join(typeDefs, "\n\n") if len(result) > 0 && len(functionDecls) > 0 { result += "\n\n" } result += strings.Join(functionDecls, "\n") return result } func RegisterBuiltin[T Args, R any](name string, fn Func[T, R]) { registerBuiltin(name, false, fn) } func RegisterAsyncBuiltin[T Args, R any](name string, fn Func[T, R]) { registerBuiltin(name, true, fn) } func GetBuiltins() map[string]Builtin { return builtinRegistry }