dynamic

package
v0.0.0-...-dac86b4 Latest Latest
Warning

This package is not in the latest version of its module.

Go to latest
Published: Feb 16, 2026 License: MIT Imports: 18 Imported by: 0

Documentation

Index

Constants

This section is empty.

Variables

View Source
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.

View Source
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

func IsPackageAllowed(pkg string) bool

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

func ValidateSource(source string) error

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

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.

func (*DynamicComponent) Start

func (dc *DynamicComponent) Start(ctx context.Context) error

Start runs the interpreted Start function.

func (*DynamicComponent) Stop

func (dc *DynamicComponent) Stop(ctx context.Context) error

Stop runs the interpreted Stop function.

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.

const (
	FieldTypeString FieldType = "string"
	FieldTypeInt    FieldType = "int"
	FieldTypeBool   FieldType = "bool"
	FieldTypeFloat  FieldType = "float"
	FieldTypeMap    FieldType = "map"
	FieldTypeSlice  FieldType = "slice"
	FieldTypeAny    FieldType = "any"
)

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.

func (*Loader) Reload

func (l *Loader) Reload(id, source string) (*DynamicComponent, error)

Reload unloads an existing component and reloads it from new 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) Execute

func (a *ModuleAdapter) Execute(ctx context.Context, params map[string]any) (map[string]any, error)

Execute delegates execution to the underlying 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) Name

func (a *ModuleAdapter) Name() string

Name returns the component name.

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

func WithAllowedPackages(pkgs map[string]bool) Option

WithAllowedPackages overrides the default allowed packages list.

func WithGoPath

func WithGoPath(path string) Option

WithGoPath sets the GOPATH for interpreters.

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.

func (*PluginWatcher) Stop

func (w *PluginWatcher) Stop() error

Stop terminates the watcher.

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.

func (*Watcher) Start

func (w *Watcher) Start() error

Start begins watching the directory for changes.

func (*Watcher) Stop

func (w *Watcher) Stop() error

Stop terminates the watcher.

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.

Jump to

Keyboard shortcuts

? : This menu
/ : Search site
f or F : Jump to
y or Y : Canonical URL