Documentation
¶
Index ¶
- Variables
- func ApplyDefaults(contract *FieldContract, params map[string]any) map[string]any
- func ExecuteWithLimits(ctx context.Context, comp *DynamicComponent, params map[string]any, ...) (map[string]any, error)
- func IsPackageAllowed(pkg string) bool
- func ValidateInputs(contract *FieldContract, params map[string]any) error
- func ValidateSource(source string) error
- type APIHandler
- type ComponentInfo
- type ComponentRegistry
- func (r *ComponentRegistry) Count() int
- func (r *ComponentRegistry) Get(id string) (*DynamicComponent, bool)
- func (r *ComponentRegistry) List() []ComponentInfo
- func (r *ComponentRegistry) ListNames() []string
- func (r *ComponentRegistry) Register(id string, component *DynamicComponent) error
- func (r *ComponentRegistry) Unregister(id string) error
- type ComponentStatus
- type ContractRegistry
- type DynamicComponent
- func (dc *DynamicComponent) Execute(ctx context.Context, params map[string]any) (map[string]any, error)
- func (dc *DynamicComponent) Info() ComponentInfo
- func (dc *DynamicComponent) Init(services map[string]any) error
- func (dc *DynamicComponent) LoadFromSource(source string) error
- func (dc *DynamicComponent) Name() string
- func (dc *DynamicComponent) Source() string
- func (dc *DynamicComponent) Start(ctx context.Context) error
- func (dc *DynamicComponent) Stop(ctx context.Context) error
- type FieldContract
- type FieldSpec
- type FieldType
- type InterpreterPool
- type Loader
- type ModuleAdapter
- func (a *ModuleAdapter) Execute(ctx context.Context, params map[string]any) (map[string]any, error)
- func (a *ModuleAdapter) Init(app modular.Application) error
- func (a *ModuleAdapter) Name() string
- func (a *ModuleAdapter) ProvidesServices() []modular.ServiceProvider
- func (a *ModuleAdapter) RequiresServices() []modular.ServiceDependency
- func (a *ModuleAdapter) SetProvides(services []string)
- func (a *ModuleAdapter) SetRequires(services []string)
- type Option
- type PluginWatcher
- type PluginWatcherOption
- type ResourceLimits
- type Watcher
- type WatcherOption
Constants ¶
This section is empty.
Variables ¶
var AllowedPackages = map[string]bool{ "fmt": true, "strings": true, "strconv": true, "encoding/json": true, "encoding/xml": true, "encoding/csv": true, "encoding/base64": true, "context": true, "time": true, "math": true, "math/rand": true, "sort": true, "sync": true, "sync/atomic": true, "errors": true, "io": true, "bytes": true, "bufio": true, "unicode": true, "unicode/utf8": true, "regexp": true, "path": true, "net/url": true, "net/http": true, "log": true, "maps": true, "slices": true, "crypto/aes": true, "crypto/cipher": true, "crypto/rand": true, "crypto/sha256": true, "crypto/hmac": true, "crypto/md5": true, "encoding/hex": true, "hash": true, "html": true, "html/template": true, "text/template": true, }
AllowedPackages defines the standard library packages that dynamically loaded components are permitted to import. Packages not in this list will be rejected during source validation.
var BlockedPackages = map[string]bool{ "os/exec": true, "syscall": true, "unsafe": true, "plugin": true, "runtime/debug": true, "reflect": true, "os": true, "net": true, "crypto/tls": true, "debug/elf": true, "debug/macho": true, "debug/pe": true, "debug/plan9obj": true, }
BlockedPackages defines packages that are explicitly forbidden for security reasons.
Functions ¶
func ApplyDefaults ¶
func ApplyDefaults(contract *FieldContract, params map[string]any) map[string]any
ApplyDefaults fills in default values from the contract for any missing optional fields in params. It returns a new map with defaults applied (does not mutate the original).
func ExecuteWithLimits ¶
func ExecuteWithLimits( ctx context.Context, comp *DynamicComponent, params map[string]any, limits ResourceLimits, ) (map[string]any, error)
ExecuteWithLimits wraps a DynamicComponent's Execute call with resource enforcement: a context-based timeout and output size validation.
func IsPackageAllowed ¶
IsPackageAllowed checks if a given import path is permitted in dynamic components.
func ValidateInputs ¶
func ValidateInputs(contract *FieldContract, params map[string]any) error
ValidateInputs checks params against the given contract. It returns an error describing all missing required fields and type mismatches found.
func ValidateSource ¶
ValidateSource performs a basic syntax check and verifies that only allowed packages are imported.
Types ¶
type APIHandler ¶
type APIHandler struct {
// contains filtered or unexported fields
}
APIHandler exposes HTTP endpoints for managing dynamic components.
func NewAPIHandler ¶
func NewAPIHandler(loader *Loader, registry *ComponentRegistry) *APIHandler
NewAPIHandler creates a new API handler.
func (*APIHandler) HandleComponentByID ¶
func (h *APIHandler) HandleComponentByID(w http.ResponseWriter, r *http.Request)
HandleComponentByID handles GET/PUT/DELETE for a component by ID. The ID is extracted as the last path segment, making this work with any URL prefix (e.g. /api/dynamic/components/{id} or /api/v1/admin/components/{id}).
func (*APIHandler) HandleComponents ¶
func (h *APIHandler) HandleComponents(w http.ResponseWriter, r *http.Request)
HandleComponents handles GET/POST /api/dynamic/components.
func (*APIHandler) RegisterRoutes ¶
func (h *APIHandler) RegisterRoutes(mux *http.ServeMux)
RegisterRoutes registers the dynamic component API routes on the given mux.
func (*APIHandler) ServeHTTP ¶
func (h *APIHandler) ServeHTTP(w http.ResponseWriter, r *http.Request)
ServeHTTP implements http.Handler for config-driven delegate dispatch. It routes to HandleComponents or HandleComponentByID based on the path.
type ComponentInfo ¶
type ComponentInfo struct {
ID string `json:"id"`
Name string `json:"name"`
Source string `json:"source,omitempty"`
Status ComponentStatus `json:"status"`
LoadedAt time.Time `json:"loaded_at"`
Error string `json:"error,omitempty"`
}
ComponentInfo holds metadata about a loaded dynamic component.
type ComponentRegistry ¶
type ComponentRegistry struct {
// contains filtered or unexported fields
}
ComponentRegistry tracks all dynamically loaded components. It is safe for concurrent access.
func NewComponentRegistry ¶
func NewComponentRegistry() *ComponentRegistry
NewComponentRegistry creates an empty registry.
func (*ComponentRegistry) Count ¶
func (r *ComponentRegistry) Count() int
Count returns the number of registered components.
func (*ComponentRegistry) Get ¶
func (r *ComponentRegistry) Get(id string) (*DynamicComponent, bool)
Get retrieves a component by ID.
func (*ComponentRegistry) List ¶
func (r *ComponentRegistry) List() []ComponentInfo
List returns info for all registered components.
func (*ComponentRegistry) ListNames ¶
func (r *ComponentRegistry) ListNames() []string
ListNames returns the IDs of all registered components.
func (*ComponentRegistry) Register ¶
func (r *ComponentRegistry) Register(id string, component *DynamicComponent) error
Register adds or replaces a component in the registry.
func (*ComponentRegistry) Unregister ¶
func (r *ComponentRegistry) Unregister(id string) error
Unregister removes a component from the registry.
type ComponentStatus ¶
type ComponentStatus string
ComponentStatus describes the lifecycle state of a dynamic component.
const ( StatusUnloaded ComponentStatus = "unloaded" StatusLoaded ComponentStatus = "loaded" StatusInitialized ComponentStatus = "initialized" StatusRunning ComponentStatus = "running" StatusStopped ComponentStatus = "stopped" StatusError ComponentStatus = "error" )
type ContractRegistry ¶
type ContractRegistry struct {
// contains filtered or unexported fields
}
ContractRegistry stores field contracts indexed by component name. It is safe for concurrent access.
func NewContractRegistry ¶
func NewContractRegistry() *ContractRegistry
NewContractRegistry creates an empty ContractRegistry.
func (*ContractRegistry) Get ¶
func (cr *ContractRegistry) Get(name string) (*FieldContract, bool)
Get retrieves the contract for a component, if one exists.
func (*ContractRegistry) List ¶
func (cr *ContractRegistry) List() []string
List returns all registered component names.
func (*ContractRegistry) Register ¶
func (cr *ContractRegistry) Register(name string, contract *FieldContract)
Register stores a contract for the given component name.
func (*ContractRegistry) Unregister ¶
func (cr *ContractRegistry) Unregister(name string)
Unregister removes a contract for the given component name.
type DynamicComponent ¶
type DynamicComponent struct {
// Contract holds the field contract extracted from the component, if declared.
Contract *FieldContract
// contains filtered or unexported fields
}
DynamicComponent wraps Yaegi-interpreted Go code as a workflow component that satisfies the modular.Module interface.
func NewDynamicComponent ¶
func NewDynamicComponent(id string, pool *InterpreterPool) *DynamicComponent
NewDynamicComponent creates a new unloaded dynamic component.
func (*DynamicComponent) Execute ¶
func (dc *DynamicComponent) Execute(ctx context.Context, params map[string]any) (map[string]any, error)
Execute runs the interpreted Execute function. If the component declares a field contract, inputs are validated and defaults applied before execution.
func (*DynamicComponent) Info ¶
func (dc *DynamicComponent) Info() ComponentInfo
Info returns the current component metadata.
func (*DynamicComponent) Init ¶
func (dc *DynamicComponent) Init(services map[string]any) error
Init satisfies modular.Module. It delegates to the interpreted Init function.
func (*DynamicComponent) LoadFromSource ¶
func (dc *DynamicComponent) LoadFromSource(source string) error
LoadFromSource compiles and loads Go source code into the component.
func (*DynamicComponent) Name ¶
func (dc *DynamicComponent) Name() string
Name returns the component name. If interpreted code provides a Name() function, that value is used; otherwise the component ID is returned.
func (*DynamicComponent) Source ¶
func (dc *DynamicComponent) Source() string
Source returns the loaded source code.
type FieldContract ¶
type FieldContract struct {
RequiredInputs map[string]FieldSpec `json:"required_inputs,omitempty"`
OptionalInputs map[string]FieldSpec `json:"optional_inputs,omitempty"`
Outputs map[string]FieldSpec `json:"outputs,omitempty"`
}
FieldContract declares the input/output field requirements for a dynamic component.
func NewFieldContract ¶
func NewFieldContract() *FieldContract
NewFieldContract creates an empty FieldContract.
type FieldSpec ¶
type FieldSpec struct {
Type FieldType `json:"type"`
Description string `json:"description,omitempty"`
Default any `json:"default,omitempty"`
}
FieldSpec describes a single field in a contract.
type FieldType ¶
type FieldType string
FieldType describes the expected type of a field in a contract.
type InterpreterPool ¶
type InterpreterPool struct {
// contains filtered or unexported fields
}
InterpreterPool manages a pool of Yaegi interpreters.
func NewInterpreterPool ¶
func NewInterpreterPool(opts ...Option) *InterpreterPool
NewInterpreterPool creates a new pool with optional configuration.
func (*InterpreterPool) NewInterpreter ¶
func (p *InterpreterPool) NewInterpreter() (*interp.Interpreter, error)
NewInterpreter creates a sandboxed Yaegi interpreter with only the allowed standard library symbols loaded.
type Loader ¶
type Loader struct {
// contains filtered or unexported fields
}
Loader handles loading dynamic components from various sources.
func NewLoader ¶
func NewLoader(pool *InterpreterPool, registry *ComponentRegistry) *Loader
NewLoader creates a Loader backed by the given pool and registry.
func (*Loader) LoadFromDirectory ¶
func (l *Loader) LoadFromDirectory(dir string) ([]*DynamicComponent, error)
LoadFromDirectory scans a directory for .go files and loads each one.
func (*Loader) LoadFromFile ¶
func (l *Loader) LoadFromFile(id, path string) (*DynamicComponent, error)
LoadFromFile reads a .go file and loads it as a component. The component ID is derived from the filename (without extension) unless the caller provides an explicit id.
func (*Loader) LoadFromString ¶
func (l *Loader) LoadFromString(id, source string) (*DynamicComponent, error)
LoadFromString validates, compiles, and registers a component from source.
type ModuleAdapter ¶
type ModuleAdapter struct {
// contains filtered or unexported fields
}
ModuleAdapter wraps a DynamicComponent as a modular.Module so it can participate in the modular dependency system.
func NewModuleAdapter ¶
func NewModuleAdapter(component *DynamicComponent) *ModuleAdapter
NewModuleAdapter creates a new ModuleAdapter wrapping the given component.
func (*ModuleAdapter) Init ¶
func (a *ModuleAdapter) Init(app modular.Application) error
Init initializes the adapter by collecting required services and passing them to the underlying component, then registering provided services.
func (*ModuleAdapter) ProvidesServices ¶
func (a *ModuleAdapter) ProvidesServices() []modular.ServiceProvider
ProvidesServices returns the services provided by this adapter.
func (*ModuleAdapter) RequiresServices ¶
func (a *ModuleAdapter) RequiresServices() []modular.ServiceDependency
RequiresServices returns the services required by this adapter.
func (*ModuleAdapter) SetProvides ¶
func (a *ModuleAdapter) SetProvides(services []string)
SetProvides sets the list of service names this adapter provides.
func (*ModuleAdapter) SetRequires ¶
func (a *ModuleAdapter) SetRequires(services []string)
SetRequires sets the list of service names this adapter requires.
type Option ¶
type Option func(*InterpreterPool)
Option configures an InterpreterPool.
func WithAllowedPackages ¶
WithAllowedPackages overrides the default allowed packages list.
type PluginWatcher ¶
type PluginWatcher struct {
// contains filtered or unexported fields
}
PluginWatcher monitors one or more plugin directories for .go file changes and hot-reloads components. Unlike the base Watcher, it supports watching multiple directories and has a dev mode for relaxed validation.
func NewPluginWatcher ¶
func NewPluginWatcher(loader *Loader, dirs []string, opts ...PluginWatcherOption) *PluginWatcher
NewPluginWatcher creates a watcher that monitors plugin directories for changes.
func (*PluginWatcher) DevMode ¶
func (w *PluginWatcher) DevMode() bool
DevMode returns whether development mode is enabled.
func (*PluginWatcher) Start ¶
func (w *PluginWatcher) Start() error
Start begins watching all plugin directories for changes.
type PluginWatcherOption ¶
type PluginWatcherOption func(*PluginWatcher)
PluginWatcherOption configures a PluginWatcher.
func WithDevMode ¶
func WithDevMode(enabled bool) PluginWatcherOption
WithDevMode enables development mode which relaxes validation for faster iteration (allows all stdlib imports and skips contract validation).
func WithOnReload ¶
func WithOnReload(fn func(id string, err error)) PluginWatcherOption
WithOnReload sets a callback invoked after a plugin is reloaded.
func WithPluginDebounce ¶
func WithPluginDebounce(d time.Duration) PluginWatcherOption
WithPluginDebounce sets the debounce duration for file change events.
func WithPluginLogger ¶
func WithPluginLogger(l *log.Logger) PluginWatcherOption
WithPluginLogger sets the logger for the watcher.
type ResourceLimits ¶
type ResourceLimits struct {
// MaxExecutionTime is the maximum duration a single Execute call may run.
// Zero means no timeout (not recommended for production).
MaxExecutionTime time.Duration
// MaxOutputSize is the maximum number of keys allowed in the Execute output map.
// Zero means unlimited.
MaxOutputSize int
}
ResourceLimits configures resource constraints for dynamic component execution.
func DefaultResourceLimits ¶
func DefaultResourceLimits() ResourceLimits
DefaultResourceLimits returns sensible defaults for production use.
func ParseResourceLimitsFromConfig ¶
func ParseResourceLimitsFromConfig(cfg map[string]any) ResourceLimits
ParseResourceLimitsFromConfig extracts ResourceLimits from a YAML config map.
type Watcher ¶
type Watcher struct {
// contains filtered or unexported fields
}
Watcher monitors a directory for .go file changes and hot-reloads components.
func NewWatcher ¶
func NewWatcher(loader *Loader, dir string, opts ...WatcherOption) *Watcher
NewWatcher creates a file system watcher that automatically reloads components when .go files in the watched directory change.
type WatcherOption ¶
type WatcherOption func(*Watcher)
WatcherOption configures a Watcher.
func WithDebounce ¶
func WithDebounce(d time.Duration) WatcherOption
WithDebounce sets the debounce duration for file change events.
func WithLogger ¶
func WithLogger(l *log.Logger) WatcherOption
WithLogger sets the logger for the watcher.