Documentation
¶
Overview ¶
Package action provides typed, composable application actions.
Build an action with New, configure it with fluent policies and middleware, then reuse the resulting BuiltAction concurrently. The typed Do method is the preferred execution path; decoded execution is intended for adapters.
Index ¶
- Constants
- Variables
- func AlwaysRetryPredicate(err error) bool
- func Async[Req, Res any](ctx context.Context, act *BuiltAction[Req, Res], req Req) <-chan AsyncResult[Res]
- func CollectStream[Req, T any](ctx context.Context, a *StreamAction[Req, T], req Req) ([]T, error)
- func ConstantBackoff(d time.Duration) func(attempt int) time.Duration
- func DefaultRetryPredicate(err error) bool
- func ExecutionIDFrom(ctx context.Context) string
- func ExponentialBackoff(base, maxDuration time.Duration) func(attempt int) time.Duration
- func ExponentialJitter(base, maxDuration time.Duration) func(attempt int) time.Duration
- func LinearBackoff(step time.Duration) func(attempt int) time.Duration
- func Race[Req, Res any](ctx context.Context, act *BuiltAction[Req, Res], reqs []Req) (Res, error)
- func SpanIDFrom(ctx context.Context) string
- func TraceIDFrom(ctx context.Context) string
- func WithExecutionID(ctx context.Context, id string) context.Context
- func WithTraceContext(ctx context.Context, traceID, spanID string) context.Context
- type ActionProvider
- type ActionScope
- type AdaptiveConfig
- type Admission
- type AnyAction
- type AnyHook
- type AsyncResult
- type AuditLogger
- type Binding
- type Bootstrapper
- type Builder
- func Branch[Req, Res any](name string, routes map[string]*Builder[Req, Res], ...) *Builder[Req, Res]
- func Chain[T any](name string, builders ...*Builder[T, T]) *Builder[T, T]
- func FirstSuccess[Req, Res any](name string, builders ...*Builder[Req, Res]) *Builder[Req, Res]
- func New[Req, Res any](name string, exec Fn[Req, Res]) *Builder[Req, Res]
- func Parallel[Req, Res any](name string, builders ...*Builder[Req, Res]) *Builder[Req, []Res]
- func Pipe[Req, Mid, Res any](name string, first *BuiltAction[Req, Mid], second *BuiltAction[Mid, Res]) *Builder[Req, Res]
- func (b *Builder[Req, Res]) Add(others ...AnyAction) *Builder[Req, Res]
- func (b *Builder[Req, Res]) AnyHook(h ...AnyHook) *Builder[Req, Res]
- func (b *Builder[Req, Res]) Audited(logger AuditLogger, category string, detailsFn func(req Req, res Res) string) *Builder[Req, Res]
- func (b *Builder[Req, Res]) Build() *BuiltAction[Req, Res]
- func (b *Builder[Req, Res]) Cache(ttl time.Duration, keyFn func(Req) string, layers ...CacheLayer[Res]) *Builder[Req, Res]
- func (b *Builder[Req, Res]) Coalesce(c *Coalescer, keyFn func(Req) string) *Builder[Req, Res]
- func (b *Builder[Req, Res]) Compose(other *BuiltAction[Req, Res]) *Builder[Req, Res]
- func (b *Builder[Req, Res]) ConcurrencyLimit(limit int32) *Builder[Req, Res]
- func (b *Builder[Req, Res]) Dedup(keyFn func(Req) string) *Builder[Req, Res]
- func (b *Builder[Req, Res]) Describe() *Meta
- func (b *Builder[Req, Res]) Description(d string) *Builder[Req, Res]
- func (b *Builder[Req, Res]) Emits(subject string, mapper func(res Res) any) *Builder[Req, Res]
- func (b *Builder[Req, Res]) Exclusive(m Mutex, ttl time.Duration, keyFn func(Req) string) *Builder[Req, Res]
- func (b *Builder[Req, Res]) ExclusiveFenced(m FencedMutex, ttl time.Duration, keyFn func(Req) string) *Builder[Req, Res]
- func (b *Builder[Req, Res]) Hook(h ...Hook[Req, Res]) *Builder[Req, Res]
- func (b *Builder[Req, Res]) HookAfter(fn func(ctx context.Context, req Req, res Res, err error, meta *Meta)) *Builder[Req, Res]
- func (b *Builder[Req, Res]) HookBefore(fn func(ctx context.Context, req Req, meta *Meta) (context.Context, error)) *Builder[Req, Res]
- func (b *Builder[Req, Res]) HookCacheHit(fn func(ctx context.Context, req Req, res Res, meta *Meta)) *Builder[Req, Res]
- func (b *Builder[Req, Res]) HookCacheMiss(fn func(ctx context.Context, req Req, meta *Meta)) *Builder[Req, Res]
- func (b *Builder[Req, Res]) HookCancel(fn func(ctx context.Context, req Req, meta *Meta)) *Builder[Req, Res]
- func (b *Builder[Req, Res]) HookError(fn func(ctx context.Context, req Req, err error, meta *Meta)) *Builder[Req, Res]
- func (b *Builder[Req, Res]) HookExecuted(fn func(ctx context.Context, req Req, res Res, meta *Meta)) *Builder[Req, Res]
- func (b *Builder[Req, Res]) HookRegister(fn func(meta *Meta)) *Builder[Req, Res]
- func (b *Builder[Req, Res]) HookRetry(fn func(ctx context.Context, req Req, attempt int, err error, meta *Meta)) *Builder[Req, Res]
- func (b *Builder[Req, Res]) Idempotent() *Builder[Req, Res]
- func (b *Builder[Req, Res]) IdempotentWithConfig(cfg IdempotencyConfig) *Builder[Req, Res]
- func (b *Builder[Req, Res]) ImmutableWhen(guard func(ctx context.Context, req Req) (bool, error), reason string) *Builder[Req, Res]
- func (b *Builder[Req, Res]) InferredResilient() *Builder[Req, Res]
- func (b *Builder[Req, Res]) Instrument(incCall func(actionName string), incError func(actionName string), ...) *Builder[Req, Res]
- func (b *Builder[Req, Res]) Internal() *Builder[Req, Res]
- func (b *Builder[Req, Res]) LeaderOnly(m Mutex, ttl time.Duration) *Builder[Req, Res]
- func (b *Builder[Req, Res]) LeaderOnlyFenced(m FencedMutex, ttl time.Duration) *Builder[Req, Res]
- func (b *Builder[Req, Res]) LogCalls(log *slog.Logger) *Builder[Req, Res]
- func (b *Builder[Req, Res]) LogSlowWhen(d time.Duration) *Builder[Req, Res]
- func (b *Builder[Req, Res]) Name(name string) *Builder[Req, Res]
- func (b *Builder[Req, Res]) Node(nodeName string) *Builder[Req, Res]
- func (b *Builder[Req, Res]) Once() *Builder[Req, Res]
- func (b *Builder[Req, Res]) Public() *Builder[Req, Res]
- func (b *Builder[Req, Res]) RateLimit(requestsPerSecond float64, burst int) *Builder[Req, Res]
- func (b *Builder[Req, Res]) RateLimitDistributed(limiter RateLimiter, keyFn func(context.Context) string) *Builder[Req, Res]
- func (b *Builder[Req, Res]) RateLimitWithKey(rps float64, burst int, keyFn func(context.Context) string) *Builder[Req, Res]
- func (b *Builder[Req, Res]) RecordHistory(capacity int) *Builder[Req, Res]
- func (b *Builder[Req, Res]) RequireAnyFeature(keys ...string) *Builder[Req, Res]
- func (b *Builder[Req, Res]) RequireAnyRole(roles ...string) *Builder[Req, Res]
- func (b *Builder[Req, Res]) RequireAuth() *Builder[Req, Res]
- func (b *Builder[Req, Res]) RequireCreationLimit(resourceName string, checkFn QuotaCheckFunc) *Builder[Req, Res]
- func (b *Builder[Req, Res]) RequireFeature(keys ...string) *Builder[Req, Res]
- func (b *Builder[Req, Res]) RequirePermission(perm string) *Builder[Req, Res]
- func (b *Builder[Req, Res]) RequireRole(role string) *Builder[Req, Res]
- func (b *Builder[Req, Res]) RequireTenant() *Builder[Req, Res]
- func (b *Builder[Req, Res]) Resilient(cfg ResilienceConfig) *Builder[Req, Res]
- func (b *Builder[Req, Res]) Retry(maxRetry int, backoff func(attempt int) time.Duration) *Builder[Req, Res]
- func (b *Builder[Req, Res]) RetryAll(maxRetry int, backoff func(attempt int) time.Duration) *Builder[Req, Res]
- func (b *Builder[Req, Res]) RetryIf(maxRetry int, backoff func(attempt int) time.Duration, ...) *Builder[Req, Res]
- func (b *Builder[Req, Res]) Route(bs ...Binding) *Builder[Req, Res]
- func (b *Builder[Req, Res]) SuccessStatus(code int) *Builder[Req, Res]
- func (b *Builder[Req, Res]) System() *Builder[Req, Res]
- func (b *Builder[Req, Res]) Tag(tags ...string) *Builder[Req, Res]
- func (b *Builder[Req, Res]) Timeout(d time.Duration) *Builder[Req, Res]
- func (b *Builder[Req, Res]) TrackPIIAccess(tracker PIITracker, purpose string) *Builder[Req, Res]
- func (b *Builder[Req, Res]) Transactional(runner TxRunner) *Builder[Req, Res]
- func (b *Builder[Req, Res]) Use(m Middleware[Req, Res]) *Builder[Req, Res]
- func (b *Builder[Req, Res]) UseWithDispatcher(m DispatcherMiddleware[Req, Res]) *Builder[Req, Res]
- func (b *Builder[Req, Res]) Validate(fn func(ctx context.Context, req Req) error) *Builder[Req, Res]
- func (b *Builder[Req, Res]) WithHistory(capacity int) (*Builder[Req, Res], *History[Req, Res])
- func (b *Builder[Req, Res]) WithProfile(profile Profile) *Builder[Req, Res]
- type BuiltAction
- func (a *BuiltAction[Req, Res]) AddAnyHook(h ...AnyHook)
- func (a *BuiltAction[Req, Res]) Describe() *Meta
- func (a *BuiltAction[Req, Res]) Do(ctx context.Context, req Req) (res Res, err error)
- func (a *BuiltAction[Req, Res]) ExecuteDecoded(ctx context.Context, decode DecodeFunc) (any, error)
- func (a *BuiltAction[Req, Res]) GetAnyHooks() []AnyHook
- func (a *BuiltAction[Req, Res]) GetBindings() []Binding
- func (a *BuiltAction[Req, Res]) GetMeta() *Meta
- func (a *BuiltAction[Req, Res]) History() *History[Req, Res]
- func (a *BuiltAction[Req, Res]) OnCacheHit(ctx context.Context, req Req, res Res)
- func (a *BuiltAction[Req, Res]) OnCacheMiss(ctx context.Context, req Req)
- func (a *BuiltAction[Req, Res]) OnCoalesced(ctx context.Context, req Req)
- func (a *BuiltAction[Req, Res]) OnDeduplicated(ctx context.Context, req Req)
- func (a *BuiltAction[Req, Res]) OnRetry(ctx context.Context, req Req, attempt int, err error)
- func (a *BuiltAction[Req, Res]) ReqPayload() any
- func (a *BuiltAction[Req, Res]) ResPayload() any
- func (a *BuiltAction[Req, Res]) String() string
- type BuiltSaga
- type CacheConfig
- type CacheLayer
- type Coalescer
- type DecodeFunc
- type Describable
- type DispatcherMiddleware
- func AdmissionMiddleware[Req, Res any](admission Admission) DispatcherMiddleware[Req, Res]
- func CacheMiddleware[Req, Res any](cfg CacheConfig[Req, Res]) DispatcherMiddleware[Req, Res]
- func CoalesceMiddleware[Req, Res any](coalescer *Coalescer, actionName string, keyFn func(Req) string) DispatcherMiddleware[Req, Res]
- func Deduplicate[Req, Res any](keyFn func(Req) string) DispatcherMiddleware[Req, Res]
- func RetryMiddleware[Req, Res any](maxRetry int, backoff func(attempt int) time.Duration) DispatcherMiddleware[Req, Res]
- func RetryWithPredicateMiddleware[Req, Res any](maxRetry int, backoff func(attempt int) time.Duration, ...) DispatcherMiddleware[Req, Res]
- type Executable
- type FencedMutex
- type Fn
- type History
- type Hook
- type HookDispatcher
- type HookProvider
- type IdempotencyClaim
- type IdempotencyClaimState
- type IdempotencyConfig
- type IdempotencyCoordinator
- type IdempotencyEntry
- type IdempotencyStore
- type LoadShedConfig
- type LockLease
- type MemoryIdempotencyStore
- func (s *MemoryIdempotencyStore) Claim(ctx context.Context, key, requestHash string, leaseTTL time.Duration) (IdempotencyClaim, error)
- func (s *MemoryIdempotencyStore) Close()
- func (s *MemoryIdempotencyStore) Complete(_ context.Context, key, token string, entry IdempotencyEntry, ...) error
- func (s *MemoryIdempotencyStore) Get(_ context.Context, key string) (IdempotencyEntry, bool)
- func (s *MemoryIdempotencyStore) Release(_ context.Context, key, token string) error
- func (s *MemoryIdempotencyStore) Set(_ context.Context, key string, entry IdempotencyEntry, ttl time.Duration)
- type MessageRes
- type Meta
- type Middleware
- func Adaptive[Req, Res any](name string, cfg AdaptiveConfig) Middleware[Req, Res]
- func AdaptiveLoadShedding[Req, Res any](stats SystemStats, cfg LoadShedConfig, p Priority) Middleware[Req, Res]
- func ConcurrencyLimitMiddleware[Req, Res any](limit int32) Middleware[Req, Res]
- func HistoryMiddleware[Req, Res any](hist *History[Req, Res]) Middleware[Req, Res]
- func SlowLogMiddleware[Req, Res any](threshold time.Duration, actionName string) Middleware[Req, Res]
- func SmartResilience[Req, Res any](name string) Middleware[Req, Res]
- func TimeoutMiddleware[Req, Res any](d time.Duration) Middleware[Req, Res]
- type Mutex
- type PIITracker
- type Plugin
- type Priority
- type Profile
- type QuotaCheckFunc
- type RateLimiter
- type Record
- type ResilienceConfig
- type RetryPredicate
- type SagaBuilder
- func (s *SagaBuilder[Req, Res]) AddOptionalStep(name string, do func(context.Context, Req) (Res, error), ...) *SagaBuilder[Req, Res]
- func (s *SagaBuilder[Req, Res]) AddStep(name string, do func(context.Context, Req) (Res, error), ...) *SagaBuilder[Req, Res]
- func (s *SagaBuilder[Req, Res]) Build() *BuiltSaga[Req, Res]
- type SagaResult
- type SagaStep
- type StateEntity
- type StateMachineBuilder
- type StepResult
- type StreamAction
- type StreamHandler
- type SystemStats
- type Testable
- func (t *Testable[Req, Res]) CaptureReq(ctx context.Context, input Req) (captured Req, res Res, err error)
- func (t *Testable[Req, Res]) Do(ctx context.Context, req Req) (Res, error)
- func (t *Testable[Req, Res]) DoRaw(ctx context.Context, req Req) (Res, error)
- func (t *Testable[Req, Res]) ExpectErr(ctx context.Context, req Req, expected string) error
- type TxRunner
- type TypedPayload
Constants ¶
const ( DefaultIdempotencyTTL = 24 * time.Hour DefaultIdempotencyLeaseTTL = 2 * time.Minute )
Variables ¶
var ErrConcurrencyLimit = errors.New("concurrency limit exceeded")
var ErrLocked = xerr.Conflict("resource is currently locked by another instance")
ErrLocked is returned when an action is already locked by another instance.
var ErrTypeAssertion = errors.New("critical type assertion failure")
Functions ¶
func AlwaysRetryPredicate ¶ added in v0.3.0
AlwaysRetryPredicate retries any non-nil error.
func Async ¶
func Async[Req, Res any](ctx context.Context, act *BuiltAction[Req, Res], req Req) <-chan AsyncResult[Res]
Async executes the action in a goroutine and returns a result channel.
func CollectStream ¶
func CollectStream[Req, T any](ctx context.Context, a *StreamAction[Req, T], req Req) ([]T, error)
CollectStream runs the entire stream and returns all items as a slice.
func ConstantBackoff ¶
ConstantBackoff waits the same duration between every attempt.
func DefaultRetryPredicate ¶ added in v0.3.0
DefaultRetryPredicate retries only errors flagged as transient by xerr.
func ExecutionIDFrom ¶
ExecutionIDFrom returns the optional correlation ID attached to ctx.
func ExponentialBackoff ¶
ExponentialBackoff returns a backoff that doubles each attempt, capped at max. 100ms → 200ms → 400ms → 800ms … → max
Usage: .Retry(3, action.ExponentialBackoff(100*time.Millisecond, 5*time.Second))
func ExponentialJitter ¶
ExponentialJitter adds ±30% random jitter to ExponentialBackoff. Prevents thundering-herd on simultaneous retries.
Usage: .Retry(3, action.ExponentialJitter(100*time.Millisecond, 5*time.Second))
func LinearBackoff ¶
LinearBackoff increments by step each attempt. 100ms → 200ms → 300ms …
func Race ¶
func Race[Req, Res any]( ctx context.Context, act *BuiltAction[Req, Res], reqs []Req, ) (Res, error)
Race executes the action concurrently for each request and returns the first success.
func SpanIDFrom ¶
SpanIDFrom returns the optional current span ID from ctx.
func TraceIDFrom ¶
TraceIDFrom returns the optional distributed trace ID from ctx.
func WithExecutionID ¶
WithExecutionID attaches an optional correlation ID to an action context. Callers should provide a request, trace, or action-execution ID at the transport or application boundary. Empty IDs are treated as absent.
Types ¶
type ActionProvider ¶
type ActionProvider interface {
Actions() []AnyAction
}
ActionProvider exposes the registered actions of an application.
type ActionScope ¶
type ActionScope string
ActionScope describes the contract audience for an action. It is not an authorization rule: authentication and authorization remain enforced by the action's transport middleware and guards.
const ( // ScopePublic is the browser/client business contract. It is the zero-value // behavior so normal client actions remain concise. ScopePublic ActionScope = "public" // ScopeInternal is a trusted service-to-service or runner contract. It is // included only in explicitly requested trusted SDK generation. ScopeInternal ActionScope = "internal" // ScopeSystem is a framework or operational contract. It is excluded from // public and trusted business contracts. ScopeSystem ActionScope = "system" )
type AdaptiveConfig ¶
type AdaptiveConfig struct {
FailureThreshold int
ResetTimeout time.Duration
InitialTimeout time.Duration
}
func (*AdaptiveConfig) SetDefaults ¶
func (c *AdaptiveConfig) SetDefaults()
type Admission ¶
Admission controls whether an execution may enter a protected section. Implementations may be local or distributed. Acquire must return an error without retaining the request when admission is denied.
type AnyAction ¶
type AnyAction interface {
Executable
Describable
GetBindings() []Binding
GetAnyHooks() []AnyHook
AddAnyHook(h ...AnyHook)
}
AnyAction is the type-erased interface for the App and Transports to handle actions.
type AnyHook ¶
type AnyHook struct {
OnRegister func(meta *Meta)
Before func(ctx context.Context, req any, meta *Meta) (context.Context, error)
After func(ctx context.Context, req any, res any, err error, meta *Meta)
OnError func(ctx context.Context, req any, err error, meta *Meta)
OnRetry func(ctx context.Context, req any, attempt int, err error, meta *Meta)
OnCacheHit func(ctx context.Context, req any, res any, meta *Meta)
OnCacheMiss func(ctx context.Context, req any, meta *Meta)
OnCoalesced func(ctx context.Context, req any, meta *Meta)
OnDeduplicated func(ctx context.Context, req any, meta *Meta)
OnCancel func(ctx context.Context, req any, meta *Meta)
OnExecuted func(ctx context.Context, req any, res any, err error, meta *Meta)
OnPanic func(ctx context.Context, req any, recovered any, meta *Meta)
}
AnyHook is used for broad plugins (metrics, tracing, auth). Passing 'meta' dynamically guarantees thread-safety and zero allocations across shared instances.
type AsyncResult ¶
AsyncResult carries the outcome of an asynchronous action execution.
func FanOut ¶
func FanOut[Req, Res any]( ctx context.Context, act *BuiltAction[Req, Res], reqs []Req, maxConcurrency int, ) []AsyncResult[Res]
FanOut executes the action concurrently for each request. maxConcurrency limits how many run simultaneously; 0 = unbounded.
type AuditLogger ¶
type AuditLogger interface {
Log(ctx context.Context, category string, actionName string, details string)
}
AuditLogger defines the minimal contract required by the builder to log audit events. This prevents circular import dependencies between action and audit adapters.
type Bootstrapper ¶
type Bootstrapper interface {
OnBoot(app ActionProvider) error
}
Bootstrapper allows plugins/actions to hook into the boot lifecycle.
type Builder ¶
type Builder[Req, Res any] struct { // contains filtered or unexported fields }
Builder[Req, Res] is the fluent construction API for an action. All methods return the same builder for chaining. Call Build() once — the result is immutable and safe for concurrent use.
func Branch ¶
func Branch[Req, Res any]( name string, routes map[string]*Builder[Req, Res], router func(context.Context, Req) (string, error), ) *Builder[Req, Res]
Branch routes a request to one of several named actions based on a router function.
func FirstSuccess ¶
FirstSuccess executes actions in order, returning the first non-error result.
func Parallel ¶
Parallel executes all actions concurrently with the same request. Concurrency pattern: Scatter-gather using sync.WaitGroup with independent result slots.
func Pipe ¶
func Pipe[Req, Mid, Res any]( name string, first *BuiltAction[Req, Mid], second *BuiltAction[Mid, Res], ) *Builder[Req, Res]
Pipe connects two actions: output of first feeds input of second. Usage:
pipe := action.Pipe[Req, Middle, Res]("order.pipe",
buildCheck, buildProcess)
result := pipe.Do(ctx, req)
func (*Builder[Req, Res]) Add ¶
Add composes AnyHooks and Bindings from other AnyActions (plugins) into this builder. Typed hooks are NOT composed — use Hook() directly for those. Safe to call multiple times; idempotent per unique plugin instance.
func (*Builder[Req, Res]) AnyHook ¶
AnyHook registers a type-erased hook (used by plugins: monitor, telemetry, tracing). Prefer typed Hook[Req,Res] when the action types are known.
func (*Builder[Req, Res]) Audited ¶
func (b *Builder[Req, Res]) Audited(logger AuditLogger, category string, detailsFn func(req Req, res Res) string) *Builder[Req, Res]
Audited attaches a generic audit hook invoked after successful action execution.
func (*Builder[Req, Res]) Build ¶
func (b *Builder[Req, Res]) Build() *BuiltAction[Req, Res]
func (*Builder[Req, Res]) Cache ¶
func (b *Builder[Req, Res]) Cache(ttl time.Duration, keyFn func(Req) string, layers ...CacheLayer[Res]) *Builder[Req, Res]
Cache adds a multi-layer cache to the action. Layers are checked in order: L1 (fast, local) → L2 (shared, e.g. Redis). On a miss, the handler runs and the result is written to all layers. On an L2 hit, L1 is back-filled automatically.
The keyFn derives a stable string cache key from the request. Keep keys short and deterministic (UUID, int, composite "tenant:id", etc.).
Example — single in-memory layer:
action.New("catalog.list", fetchCatalog).
Cache(30*time.Minute,
func(_ CatalogReq) string { return "catalog:all" },
cache.NewInMemory[[]Product](30*time.Minute),
).Build()
Example — L1 memory + L2 Redis:
action.New("product.get", fetchProduct).
Cache(10*time.Minute,
func(r ProductReq) string { return r.ProductID },
cache.NewInMemory[Product](5*time.Minute),
cache.NewRedis[Product](redisClient),
).Build()
func (*Builder[Req, Res]) Coalesce ¶
Coalesce deduplicates concurrent requests using request coalescing. Unlike Dedup, Coalesce allows a caller whose context is canceled to bail out early without killing the underlying in-flight request — the in-flight request continues for other waiters.
A single *Coalescer can be shared across multiple actions:
c := action.NewCoalescer()
productAct := action.New("product.get", fetchProduct).
Coalesce(c, func(r ProductReq) string { return r.ProductID }).
Build()
inventoryAct := action.New("inventory.check", checkStock).
Coalesce(c, func(r InventoryReq) string { return r.SKU }).
Build()
func (*Builder[Req, Res]) Compose ¶
func (b *Builder[Req, Res]) Compose(other *BuiltAction[Req, Res]) *Builder[Req, Res]
Compose copies typed hooks, AnyHooks, and Bindings from another BuiltAction with the same Req/Res types into this builder.
func (*Builder[Req, Res]) ConcurrencyLimit ¶
func (*Builder[Req, Res]) Dedup ¶
Dedup prevents concurrent identical requests from executing multiple times. All callers with the same key block until the first one completes, then share its result — exactly one handler invocation per unique key at any point in time.
keyFn returns the dedup key. An empty string disables dedup for that request.
Use case — prevent N concurrent callers from all hitting the DB for the same product:
productAct := action.New("product.price", fetchPrice).
Dedup(func(r PriceReq) string { return r.ProductID }).
Build()
func (*Builder[Req, Res]) Description ¶
func (*Builder[Req, Res]) Emits ¶
Emits registers automatic event emission upon successful action execution. Payload mapping is evaluated lazily only when an active EventPublisher is in context.
func (*Builder[Req, Res]) Exclusive ¶
func (b *Builder[Req, Res]) Exclusive(m Mutex, ttl time.Duration, keyFn func(Req) string) *Builder[Req, Res]
Exclusive wraps an action with a standard distributed lock. If the lock cannot be acquired, ErrLocked is returned immediately.
func (*Builder[Req, Res]) ExclusiveFenced ¶
func (b *Builder[Req, Res]) ExclusiveFenced(m FencedMutex, ttl time.Duration, keyFn func(Req) string) *Builder[Req, Res]
ExclusiveFenced runs an action under an ownership-checked lease. The lease is renewed while the action is running; loss of the lease cancels the action context and returns an unavailable error rather than claiming success.
func (*Builder[Req, Res]) Hook ¶
Hook registers a full typed hook struct. Use when you need more than one hook event in a single declaration.
func (*Builder[Req, Res]) HookAfter ¶
func (b *Builder[Req, Res]) HookAfter(fn func(ctx context.Context, req Req, res Res, err error, meta *Meta)) *Builder[Req, Res]
HookAfter runs fn after execution regardless of success or failure. Use for audit logging, always-on cleanup, unconditional metrics.
func (*Builder[Req, Res]) HookBefore ¶
func (b *Builder[Req, Res]) HookBefore(fn func(ctx context.Context, req Req, meta *Meta) (context.Context, error)) *Builder[Req, Res]
HookBefore runs fn before the handler. Can enrich ctx or abort with an error. Aborting returns the error immediately — the handler never runs.
func (*Builder[Req, Res]) HookCacheHit ¶
func (b *Builder[Req, Res]) HookCacheHit(fn func(ctx context.Context, req Req, res Res, meta *Meta)) *Builder[Req, Res]
HookCacheHit runs fn when the CacheMiddleware serves a response from cache. The handler is NOT called in this case. Use for cache-hit metrics, hit-rate logging.
func (*Builder[Req, Res]) HookCacheMiss ¶
func (b *Builder[Req, Res]) HookCacheMiss(fn func(ctx context.Context, req Req, meta *Meta)) *Builder[Req, Res]
HookCacheMiss runs fn when the CacheMiddleware finds no cached result. The handler will be called immediately after. Use for cache-miss metrics, warming triggers.
func (*Builder[Req, Res]) HookCancel ¶
func (b *Builder[Req, Res]) HookCancel(fn func(ctx context.Context, req Req, meta *Meta)) *Builder[Req, Res]
HookCancel runs fn when the request context is canceled (client disconnect, upstream timeout, explicit cancel). Use for cleanup, releasing reserved resources, cancellation metrics.
func (*Builder[Req, Res]) HookError ¶
func (b *Builder[Req, Res]) HookError(fn func(ctx context.Context, req Req, err error, meta *Meta)) *Builder[Req, Res]
HookError runs fn only when the handler returns a non-nil error. Use for alerting, dead-letter queues, structured error logging.
func (*Builder[Req, Res]) HookExecuted ¶
func (b *Builder[Req, Res]) HookExecuted(fn func(ctx context.Context, req Req, res Res, meta *Meta)) *Builder[Req, Res]
HookExecuted runs fn only on successful execution (err == nil). The fn signature omits err for convenience — it is always nil here. Use for domain-event publishing, analytics, cache warming.
func (*Builder[Req, Res]) HookRegister ¶
HookRegister runs fn once at Build() time — never on the hot path. Use to pre-allocate metric labels, register with service discovery, validate configuration, or build static routing tables.
Example — pre-allocate Prometheus labels:
.HookRegister(func(meta *action.Meta) {
requestCounter.WithLabelValues(meta.Name) // pre-allocate label set
})
func (*Builder[Req, Res]) HookRetry ¶
func (b *Builder[Req, Res]) HookRetry(fn func(ctx context.Context, req Req, attempt int, err error, meta *Meta)) *Builder[Req, Res]
HookRetry runs fn before each retry attempt made by the Retry middleware. attempt starts at 1 for the first retry. Use for retry-specific logging, jitter metrics, backoff tracing.
func (*Builder[Req, Res]) Idempotent ¶
func (*Builder[Req, Res]) IdempotentWithConfig ¶
func (b *Builder[Req, Res]) IdempotentWithConfig(cfg IdempotencyConfig) *Builder[Req, Res]
func (*Builder[Req, Res]) ImmutableWhen ¶
func (b *Builder[Req, Res]) ImmutableWhen(guard func(ctx context.Context, req Req) (bool, error), reason string) *Builder[Req, Res]
ImmutableWhen aborts execution with 403 Forbidden if the guard condition returns true. Useful for locking entities in terminal or processed states (e.g. settled invoices).
func (*Builder[Req, Res]) InferredResilient ¶
InferredResilient applies the smart defaults to a Builder
func (*Builder[Req, Res]) Instrument ¶
func (b *Builder[Req, Res]) Instrument( incCall func(actionName string), incError func(actionName string), recordLatency func(actionName string, ms float64), ) *Builder[Req, Res]
Instrument wires a minimal set of Prometheus-style counters via AnyHook. Counts are tracked by calling the provided inc functions — no Prometheus import required, works with any counter abstraction.
Example with Prometheus:
calls := prometheus.NewCounterVec(...)
errors := prometheus.NewCounterVec(...)
latency := prometheus.NewHistogramVec(...)
act := action.New("order.create", handler).
Instrument(
func(name string) { calls.WithLabelValues(name).Inc() },
func(name string) { errors.WithLabelValues(name).Inc() },
func(name string, ms float64) { latency.WithLabelValues(name).Observe(ms / 1000) },
).Build()
func (*Builder[Req, Res]) Internal ¶
Internal exposes this action only through an explicitly requested trusted service-to-service or runner contract.
func (*Builder[Req, Res]) LeaderOnly ¶
LeaderOnly restricts action execution to a single instance using a global leader key.
func (*Builder[Req, Res]) LeaderOnlyFenced ¶
func (b *Builder[Req, Res]) LeaderOnlyFenced(m FencedMutex, ttl time.Duration) *Builder[Req, Res]
LeaderOnlyFenced is the safe singleton-action form. It provides a renewable ownership lease, not merely a best-effort process-local convention.
func (*Builder[Req, Res]) LogCalls ¶
LogCalls injects a structured log entry for every call using the provided logger. Logs before the call (level=Debug) and after (level=Info on success, Error on failure).
This is a lightweight alternative to telemetry.New() for simple deployments.
debugAct := action.New("order.create", handler).
LogCalls(slog.Default()).
Build()
func (*Builder[Req, Res]) LogSlowWhen ¶
func (*Builder[Req, Res]) Once ¶
Once caches the result of the first successful execution and returns it for all subsequent calls. Errors are also cached – the action will not retry. Use only for idempotent, read‑only actions (e.g., static config generation).
The first caller determines the cached result; later callers receive the same value or error without re‑executing the handler. Context cancellation is ignored after the first execution.
Example:
action.New("config.generate", generator).
Once().
Route(thttp.GET("/config.json")).
Build()
func (*Builder[Req, Res]) Public ¶
Public exposes this action through the browser/client business contract. It does not weaken authentication or authorization requirements.
func (*Builder[Req, Res]) RateLimitDistributed ¶
func (*Builder[Req, Res]) RateLimitWithKey ¶
func (*Builder[Req, Res]) RecordHistory ¶
WithHistory attaches a ring-buffer execution history to the action AND returns the *History[Req, Res] handle for inspection. Replaces the two-return-value .WithHistory pattern with a single method that stores the handle on the builder for later retrieval.
The built action keeps the last cap records (newest-first via Snapshot).
Usage:
paymentAct := action.New("payment.charge", chargeCard).
RecordHistory(200).
Build()
hist := paymentAct.History() // nil if RecordHistory was not called
func (*Builder[Req, Res]) RequireAnyFeature ¶
RequireAnyFeature aborts with 403 Forbidden if NONE of the listed feature flags are enabled in ctx (logical OR — at least one must be on).
betaOrPremiumAct := action.New("widget.beta", handler).
RequireAnyFeature("beta-access", "premium-plan").
Build()
func (*Builder[Req, Res]) RequireAnyRole ¶
RequireAnyRole aborts with 403 Forbidden if ctx contains none of the given roles. First matching role passes — order does not matter.
approveRefundAct := action.New("refund.approve", handler).
RequireAnyRole("admin", "finance", "support").
Build()
func (*Builder[Req, Res]) RequireAuth ¶
RequireAuth aborts with 401 Unauthorized if ctx has no authenticated user (UserID is empty). Use when an endpoint requires login but any role is fine.
myProfileAct := action.New("user.me", handler).
RequireAuth().
Build()
func (*Builder[Req, Res]) RequireCreationLimit ¶
func (b *Builder[Req, Res]) RequireCreationLimit(resourceName string, checkFn QuotaCheckFunc) *Builder[Req, Res]
RequireCreationLimit enforces plan quotas ONLY during new entity creation (ID == 0). Updates to existing resources bypass this check.
func (*Builder[Req, Res]) RequireFeature ¶
RequireFeature aborts with 403 Forbidden if ALL listed feature flags are not enabled in ctx (logical AND — every flag must be on). Feature flags are injected from the "features" JWT claim.
Single flag:
aiCheckoutAct := action.New("checkout.ai", handler).
RequireFeature("ai-checkout").
Build()
Multiple flags (all must be enabled):
premiumExportAct := action.New("export.premium", handler).
RequireFeature("premium-plan", "data-export").
Build()
func (*Builder[Req, Res]) RequirePermission ¶
RequirePermission aborts with 403 Forbidden if ctx does not contain the given fine-grained permission string. Permissions are injected from the "perms" JWT claim.
exportAct := action.New("data.export", handler).
RequirePermission("data:export").
Build()
func (*Builder[Req, Res]) RequireRole ¶
RequireRole aborts with 403 Forbidden if ctx does not contain the given role. Roles are injected by the JWT middleware from the "roles" claim.
deleteOrderAct := action.New("order.delete", handler).
RequireRole("admin").
Route(thttp.DELETE("/api/v1/orders/{id}")).
Build()
func (*Builder[Req, Res]) RequireTenant ¶
RequireTenant aborts with 401 Unauthorized if ctx has no tenant ID. Use to guard multi-tenant endpoints from unauthenticated callers.
tenantOrderAct := action.New("order.list", handler).
RequireTenant().
Build()
func (*Builder[Req, Res]) Resilient ¶
func (b *Builder[Req, Res]) Resilient(cfg ResilienceConfig) *Builder[Req, Res]
func (*Builder[Req, Res]) Retry ¶
func (b *Builder[Req, Res]) Retry( maxRetry int, backoff func(attempt int) time.Duration, ) *Builder[Req, Res]
Retry retries only transient errors detected by xerr.IsTransient.
func (*Builder[Req, Res]) RetryAll ¶ added in v0.3.0
func (b *Builder[Req, Res]) RetryAll( maxRetry int, backoff func(attempt int) time.Duration, ) *Builder[Req, Res]
RetryAll retries on ANY non-nil error. Use only for idempotent jobs, scripts, and safe batch operations.
func (*Builder[Req, Res]) RetryIf ¶ added in v0.3.0
func (b *Builder[Req, Res]) RetryIf( maxRetry int, backoff func(attempt int) time.Duration, predicate RetryPredicate, ) *Builder[Req, Res]
RetryIf retries when the supplied predicate returns true.
func (*Builder[Req, Res]) SuccessStatus ¶
func (*Builder[Req, Res]) System ¶
System marks a framework or operations-plane action. It is excluded from public and trusted business contracts.
func (*Builder[Req, Res]) TrackPIIAccess ¶
func (b *Builder[Req, Res]) TrackPIIAccess(tracker PIITracker, purpose string) *Builder[Req, Res]
TrackPIIAccess attaches a non-blocking hook recording the purpose of PII data access.
func (*Builder[Req, Res]) Transactional ¶
func (*Builder[Req, Res]) Use ¶
func (b *Builder[Req, Res]) Use(m Middleware[Req, Res]) *Builder[Req, Res]
func (*Builder[Req, Res]) UseWithDispatcher ¶
func (b *Builder[Req, Res]) UseWithDispatcher(m DispatcherMiddleware[Req, Res]) *Builder[Req, Res]
func (*Builder[Req, Res]) Validate ¶
func (b *Builder[Req, Res]) Validate(fn func(ctx context.Context, req Req) error) *Builder[Req, Res]
Validate adds a request validation middleware to the action construction pipeline.
func (*Builder[Req, Res]) WithHistory ¶
func (*Builder[Req, Res]) WithProfile ¶
WithProfile applies a policy bundle before optional action-specific overrides. The returned builder remains fully fluent, so exceptional actions can explicitly refine timeout, route, or idempotency configuration.
type BuiltAction ¶
type BuiltAction[Req, Res any] struct { // contains filtered or unexported fields }
func (*BuiltAction[Req, Res]) AddAnyHook ¶
func (a *BuiltAction[Req, Res]) AddAnyHook(h ...AnyHook)
func (*BuiltAction[Req, Res]) Describe ¶
func (a *BuiltAction[Req, Res]) Describe() *Meta
func (*BuiltAction[Req, Res]) Do ¶
func (a *BuiltAction[Req, Res]) Do(ctx context.Context, req Req) (res Res, err error)
func (*BuiltAction[Req, Res]) ExecuteDecoded ¶
func (a *BuiltAction[Req, Res]) ExecuteDecoded(ctx context.Context, decode DecodeFunc) (any, error)
func (*BuiltAction[Req, Res]) GetAnyHooks ¶
func (a *BuiltAction[Req, Res]) GetAnyHooks() []AnyHook
func (*BuiltAction[Req, Res]) GetBindings ¶
func (a *BuiltAction[Req, Res]) GetBindings() []Binding
func (*BuiltAction[Req, Res]) GetMeta ¶
func (a *BuiltAction[Req, Res]) GetMeta() *Meta
func (*BuiltAction[Req, Res]) History ¶
func (a *BuiltAction[Req, Res]) History() *History[Req, Res]
func (*BuiltAction[Req, Res]) OnCacheHit ¶
func (a *BuiltAction[Req, Res]) OnCacheHit(ctx context.Context, req Req, res Res)
func (*BuiltAction[Req, Res]) OnCacheMiss ¶
func (a *BuiltAction[Req, Res]) OnCacheMiss(ctx context.Context, req Req)
func (*BuiltAction[Req, Res]) OnCoalesced ¶
func (a *BuiltAction[Req, Res]) OnCoalesced(ctx context.Context, req Req)
func (*BuiltAction[Req, Res]) OnDeduplicated ¶
func (a *BuiltAction[Req, Res]) OnDeduplicated(ctx context.Context, req Req)
func (*BuiltAction[Req, Res]) OnRetry ¶
func (a *BuiltAction[Req, Res]) OnRetry(ctx context.Context, req Req, attempt int, err error)
func (*BuiltAction[Req, Res]) ReqPayload ¶
func (a *BuiltAction[Req, Res]) ReqPayload() any
func (*BuiltAction[Req, Res]) ResPayload ¶
func (a *BuiltAction[Req, Res]) ResPayload() any
func (*BuiltAction[Req, Res]) String ¶
func (a *BuiltAction[Req, Res]) String() string
type BuiltSaga ¶
type BuiltSaga[Req, Res any] struct { // contains filtered or unexported fields }
BuiltSaga is the immutable, executable Saga.
type CacheConfig ¶
type CacheLayer ¶
type Coalescer ¶
type Coalescer struct {
// contains filtered or unexported fields
}
func NewCoalescer ¶
func NewCoalescer() *Coalescer
type DecodeFunc ¶
DecodeFunc allows transports to inject data directly into the concrete type.
type Describable ¶
type Describable interface {
Describe() *Meta
}
Describable handles the COLD path — boot, discovery, routing, and CLI help.
type DispatcherMiddleware ¶
type DispatcherMiddleware[Req, Res any] func(next Fn[Req, Res], hooks HookDispatcher[Req, Res]) Fn[Req, Res]
func AdmissionMiddleware ¶
func AdmissionMiddleware[Req, Res any](admission Admission) DispatcherMiddleware[Req, Res]
AdmissionMiddleware applies an external admission policy around execution. It keeps the policy outside Builder while allowing typed middleware composition and automatic request/response inference.
func CacheMiddleware ¶
func CacheMiddleware[Req, Res any](cfg CacheConfig[Req, Res]) DispatcherMiddleware[Req, Res]
CacheMiddleware implements the Read-Through / Write-Behind pattern with isolated singleflight execution.
func CoalesceMiddleware ¶
func CoalesceMiddleware[Req, Res any](coalescer *Coalescer, actionName string, keyFn func(Req) string) DispatcherMiddleware[Req, Res]
func Deduplicate ¶
func Deduplicate[Req, Res any](keyFn func(Req) string) DispatcherMiddleware[Req, Res]
func RetryMiddleware ¶
func RetryMiddleware[Req, Res any]( maxRetry int, backoff func(attempt int) time.Duration, ) DispatcherMiddleware[Req, Res]
RetryMiddleware is the backward-compatible, safe default retry middleware. It retries only transient errors detected by xerr.IsTransient.
func RetryWithPredicateMiddleware ¶ added in v0.3.0
func RetryWithPredicateMiddleware[Req, Res any]( maxRetry int, backoff func(attempt int) time.Duration, predicate RetryPredicate, ) DispatcherMiddleware[Req, Res]
RetryWithPredicateMiddleware retries errors when predicate returns true. If predicate is nil, it falls back to DefaultRetryPredicate. If backoff is nil, it defaults to ConstantBackoff(0). If maxRetry is negative, it is clamped to 0.
type Executable ¶
type Executable interface {
ExecuteDecoded(ctx context.Context, decode DecodeFunc) (any, error)
}
Executable handles the HOT path — execution only.
type FencedMutex ¶
type FencedMutex interface {
Acquire(ctx context.Context, key string, ttl time.Duration) (lease LockLease, acquired bool, err error)
Renew(ctx context.Context, lease LockLease, ttl time.Duration) (renewed bool, err error)
Release(ctx context.Context, lease LockLease) (released bool, err error)
}
FencedMutex is the production-safe distributed coordination contract. A lease belongs to exactly one owner, can be renewed only by that owner, and can be released only by that owner.
type History ¶
type History[Req, Res any] struct { // contains filtered or unexported fields }
func NewHistory ¶
type Hook ¶
type Hook[Req, Res any] struct { OnRegister func(meta *Meta) Before func(ctx context.Context, req Req, meta *Meta) (context.Context, error) After func(ctx context.Context, req Req, res Res, err error, meta *Meta) OnError func(ctx context.Context, req Req, err error, meta *Meta) OnRetry func(ctx context.Context, req Req, attempt int, err error, meta *Meta) OnCacheHit func(ctx context.Context, req Req, res Res, meta *Meta) OnCacheMiss func(ctx context.Context, req Req, meta *Meta) OnCoalesced func(ctx context.Context, req Req, meta *Meta) OnDeduplicated func(ctx context.Context, req Req, meta *Meta) OnCancel func(ctx context.Context, req Req, meta *Meta) OnExecuted func(ctx context.Context, req Req, res Res, err error, meta *Meta) }
type HookDispatcher ¶
type HookProvider ¶
type HookProvider interface {
GetAnyHooks() []AnyHook
}
type IdempotencyClaim ¶
type IdempotencyClaim struct {
State IdempotencyClaimState
Token string
Entry IdempotencyEntry
}
IdempotencyClaim is returned by an atomic coordinator. Token is populated only for an acquired claim and must be presented to Complete or Release.
type IdempotencyClaimState ¶
type IdempotencyClaimState uint8
IdempotencyClaimState describes one atomic attempt to own an idempotency key. It is deliberately business-neutral: transports decide whether an in-progress request should be retried, polled, or reported to a caller.
const ( IdempotencyClaimAcquired IdempotencyClaimState = iota + 1 IdempotencyClaimCompleted IdempotencyClaimInProgress IdempotencyClaimConflict )
type IdempotencyConfig ¶
type IdempotencyConfig struct {
// Enabled activates idempotency for this action.
Enabled bool
// TTL overrides the store's default TTL for entries of this action.
// 0 = use store default (24 h).
TTL time.Duration
// KeyHeader is the HTTP header to read the key from.
// Defaults to "Idempotency-Key".
KeyHeader string
// KeyFunc derives the idempotency key from the raw request body bytes.
// Useful when the key lives inside JSON rather than a header.
// When nil, only KeyHeader is used.
KeyFunc func(body []byte) string
// LeaseTTL bounds one in-progress owner when a store provides atomic
// coordination. Zero uses DefaultIdempotencyLeaseTTL. It must exceed the
// action's worst-case execution time; it is not the completed replay TTL.
LeaseTTL time.Duration
}
IdempotencyConfig controls per-action idempotency behavior. Zero value = disabled. Attach via .Idempotent() or .IdempotentWithConfig().
func (IdempotencyConfig) EffectiveLeaseTTL ¶
func (c IdempotencyConfig) EffectiveLeaseTTL() time.Duration
EffectiveLeaseTTL returns the configured lease or the documented default.
func (IdempotencyConfig) Header ¶
func (c IdempotencyConfig) Header() string
Header returns the effective header name (never empty).
type IdempotencyCoordinator ¶
type IdempotencyCoordinator interface {
IdempotencyStore
Claim(ctx context.Context, key, requestHash string, leaseTTL time.Duration) (IdempotencyClaim, error)
Complete(ctx context.Context, key, token string, entry IdempotencyEntry, ttl time.Duration) error
Release(ctx context.Context, key, token string) error
}
IdempotencyCoordinator is an optional stronger capability implemented by a durable store. Claim must atomically create an in-progress owner or return the already stored state. Complete and Release must affect only a claim held by the supplied opaque token.
This protects duplicate execution while a valid lease is held. It does not make an arbitrary external side effect globally exactly-once; use a transactional business write/outbox where that guarantee is required.
type IdempotencyEntry ¶
type IdempotencyEntry struct {
Status int
Body []byte
Headers map[string]string // safe headers only: Content-Type, X-Request-ID
StoredAt time.Time
RequestHash string
}
IdempotencyEntry is the captured response for a completed idempotent request.
type IdempotencyStore ¶
type IdempotencyStore interface {
Get(ctx context.Context, key string) (IdempotencyEntry, bool)
Set(ctx context.Context, key string, entry IdempotencyEntry, ttl time.Duration)
}
IdempotencyStore persists and retrieves idempotency entries. Implement this interface backed by Redis for multi-node deployments. The default MemoryIdempotencyStore is suitable for single-node / dev.
type LoadShedConfig ¶
type LockLease ¶
LockLease proves ownership of a distributed lock. Fence is monotonically increasing for a key and must be carried to any downstream system that can reject stale writers.
type MemoryIdempotencyStore ¶
type MemoryIdempotencyStore struct {
// contains filtered or unexported fields
}
MemoryIdempotencyStore provides an in-memory implementation of IdempotencyCoordinator with background TTL eviction and atomic lease claims.
func NewMemoryIdempotencyStore ¶
func NewMemoryIdempotencyStore(defTTL time.Duration) *MemoryIdempotencyStore
NewMemoryIdempotencyStore creates a store with background TTL eviction. defTTL 0 → 24 h.
func (*MemoryIdempotencyStore) Claim ¶ added in v0.2.0
func (s *MemoryIdempotencyStore) Claim(ctx context.Context, key, requestHash string, leaseTTL time.Duration) (IdempotencyClaim, error)
func (*MemoryIdempotencyStore) Close ¶
func (s *MemoryIdempotencyStore) Close()
Close stops the eviction goroutine. Safe to call multiple times.
func (*MemoryIdempotencyStore) Complete ¶ added in v0.2.0
func (s *MemoryIdempotencyStore) Complete(_ context.Context, key, token string, entry IdempotencyEntry, ttl time.Duration) error
func (*MemoryIdempotencyStore) Get ¶
func (s *MemoryIdempotencyStore) Get(_ context.Context, key string) (IdempotencyEntry, bool)
func (*MemoryIdempotencyStore) Release ¶ added in v0.2.0
func (s *MemoryIdempotencyStore) Release(_ context.Context, key, token string) error
func (*MemoryIdempotencyStore) Set ¶
func (s *MemoryIdempotencyStore) Set(_ context.Context, key string, entry IdempotencyEntry, ttl time.Duration)
type MessageRes ¶
type MessageRes struct {
Message string `json:"message"`
}
MessageRes is a standard DTO for actions that only need to return a text message. Using a strongly-typed struct instead of map[string]string ensures precise SDK generation and clean OpenAPI documentation.
type Meta ¶
type Meta struct {
Name string `json:"name"`
Description string `json:"description,omitempty"`
Node string `json:"node,omitempty"`
Tags []string `json:"tags,omitempty"`
Scope ActionScope `json:"scope,omitempty"`
Idempotency IdempotencyConfig `json:"idempotency"`
SuccessStatus int `json:"success_status,omitempty"`
LogSlowThreshold time.Duration `json:"log_slow_threshold,omitempty"`
RequiredRoles []string `json:"required_roles,omitempty"`
RequiredPermissions []string `json:"required_permissions,omitempty"`
RequiredFeatures []string `json:"required_features,omitempty"`
RequiresAuth bool `json:"requires_auth,omitempty"`
RetryMax int `json:"retry_max,omitempty"`
Timeout time.Duration `json:"timeout,omitempty"`
ConcurrencyLimit int32 `json:"concurrency_limit,omitempty"`
RateLimit string `json:"rate_limit,omitempty"`
CacheTTL time.Duration `json:"cache_ttl,omitempty"`
Deduplicated bool `json:"deduplicated,omitempty"`
Coalesced bool `json:"coalesced,omitempty"`
}
func (*Meta) IsInternal ¶
IsInternal reports whether the action belongs only to trusted callers.
func (*Meta) IsPublic ¶
IsPublic reports whether the action is part of the public business contract. The zero value is public for concise ordinary client actions.
type Middleware ¶
func Adaptive ¶
func Adaptive[Req, Res any](name string, cfg AdaptiveConfig) Middleware[Req, Res]
func AdaptiveLoadShedding ¶
func AdaptiveLoadShedding[Req, Res any](stats SystemStats, cfg LoadShedConfig, p Priority) Middleware[Req, Res]
AdaptiveLoadShedding drops traffic instantly if the server is physically choking.
func ConcurrencyLimitMiddleware ¶
func ConcurrencyLimitMiddleware[Req, Res any](limit int32) Middleware[Req, Res]
func HistoryMiddleware ¶
func HistoryMiddleware[Req, Res any](hist *History[Req, Res]) Middleware[Req, Res]
func SlowLogMiddleware ¶ added in v0.2.0
func SlowLogMiddleware[Req, Res any](threshold time.Duration, actionName string) Middleware[Req, Res]
func SmartResilience ¶
func SmartResilience[Req, Res any](name string) Middleware[Req, Res]
SmartResilience automatically applies intelligent backoff and circuit breaking based entirely on the xerr.Kind of the returned error. It embodies the "Inferred Resilience" pattern.
func TimeoutMiddleware ¶
func TimeoutMiddleware[Req, Res any](d time.Duration) Middleware[Req, Res]
TimeoutMiddleware uses standard Middleware.
type Mutex ¶
type Mutex interface {
TryLock(ctx context.Context, key string, ttl time.Duration) (bool, error)
Unlock(ctx context.Context, key string) error
}
Mutex defines a standard distributed lock contract for critical sections. For systems requiring protection against stale writers during process pauses, prefer FencedMutex via ExclusiveFenced.
type PIITracker ¶
type PIITracker interface {
TrackPIIAccess(ctx context.Context, purpose string, actionName string)
}
PIITracker defines the minimal contract for personal data access tracking.
type Plugin ¶
type Plugin interface {
HookProvider
ActionProvider
}
type Profile ¶
type Profile struct {
Tags []string
Scope ActionScope
Timeout time.Duration
ConcurrencyLimit int32
SuccessStatus int
RequireAuth bool
RequiredPermissions []string
Idempotency *IdempotencyConfig
}
Profile is a named bundle of action metadata and middleware defaults. It deliberately excludes a route and description: those are action-specific contract details and should remain visible at registration sites.
func AuthenticatedReadProfile ¶
AuthenticatedReadProfile is the conservative default for an authenticated, read-only query. It intentionally does not enable idempotency because no side effect exists to replay.
func IdempotentCommandProfile ¶
IdempotentCommandProfile is the default for a mutation that can safely replay the same request key. Business code remains responsible for durable transactions and external-side-effect coordination.
func InternalEventProfile ¶
InternalEventProfile is for service-to-service ingestion routes. It requires an authenticated transport identity and idempotency, but makes no claim about the application-specific identity verifier installed by the service.
type QuotaCheckFunc ¶
type RateLimiter ¶
type ResilienceConfig ¶
type ResilienceConfig struct {
MaxRetries int
Backoff func(attempt int) time.Duration
Predicate RetryPredicate
Timeout time.Duration
MaxConcurrent int32
Adaptive *AdaptiveConfig
}
type RetryPredicate ¶ added in v0.3.0
RetryPredicate determines whether a given error warrants an execution retry.
type SagaBuilder ¶
type SagaBuilder[Req, Res any] struct { // contains filtered or unexported fields }
SagaBuilder constructs a distributed transaction pipeline.
func NewSaga ¶
func NewSaga[Req, Res any](name string) *SagaBuilder[Req, Res]
NewSaga initializes an in-memory Saga pipeline.
func (*SagaBuilder[Req, Res]) AddOptionalStep ¶
func (s *SagaBuilder[Req, Res]) AddOptionalStep( name string, do func(context.Context, Req) (Res, error), undo func(context.Context, Req) error, ) *SagaBuilder[Req, Res]
AddOptionalStep appends a step that skips on failure without triggering a Saga rollback.
func (*SagaBuilder[Req, Res]) AddStep ¶
func (s *SagaBuilder[Req, Res]) AddStep( name string, do func(context.Context, Req) (Res, error), undo func(context.Context, Req) error, ) *SagaBuilder[Req, Res]
AddStep appends a mandatory (Do, Undo) pair.
func (*SagaBuilder[Req, Res]) Build ¶
func (s *SagaBuilder[Req, Res]) Build() *BuiltSaga[Req, Res]
Build compiles the Saga.
type SagaResult ¶
type SagaResult[Res any] struct { Saga string `json:"saga"` Success bool `json:"success"` Output Res `json:"output,omitempty"` Steps []StepResult `json:"steps"` Error string `json:"error,omitempty"` RolledBack bool `json:"rolled_back,omitempty"` DurationMs int64 `json:"duration_ms"` }
SagaResult captures the full execution audit and output of the Saga.
type SagaStep ¶
type SagaStep[Req, Res any] struct { Name string Do func(context.Context, Req) (Res, error) Undo func(context.Context, Req) error Optional bool }
SagaStep represents a single operation and its compensating rollback.
type StateEntity ¶
StateEntity defines an interface for structs that possess a lifecycle state.
type StateMachineBuilder ¶
type StateMachineBuilder[Req StateEntity, Res any] struct { // contains filtered or unexported fields }
StateMachineBuilder provides a fluent DSL for building state-guarded actions.
func NewStateMachine ¶
func NewStateMachine[Req StateEntity, Res any](name string, exec Fn[Req, Res]) *StateMachineBuilder[Req, Res]
NewStateMachine creates an action that strictly guards state transitions.
func (*StateMachineBuilder[Req, Res]) Allow ¶
func (sm *StateMachineBuilder[Req, Res]) Allow(from string, to ...string) *StateMachineBuilder[Req, Res]
Allow maps a valid state transition. Example: .Allow("pending", "paid", "canceled")
func (*StateMachineBuilder[Req, Res]) Build ¶
func (sm *StateMachineBuilder[Req, Res]) Build() *BuiltAction[Req, Res]
Build compiles the State Machine into a standard Nexss Builder, injecting the validation middleware automatically.
type StepResult ¶
type StepResult struct {
Step string `json:"step"`
Success bool `json:"success"`
Skipped bool `json:"skipped,omitempty"`
Error string `json:"error,omitempty"`
DurationMs int64 `json:"duration_ms"`
}
StepResult captures execution metadata for a single Saga step.
type StreamAction ¶
type StreamAction[Req, T any] struct { // contains filtered or unexported fields }
StreamAction wraps a streaming handler with the standard middleware chain.
func NewStream ¶
func NewStream[Req, T any](name string, h StreamHandler[Req, T]) *StreamAction[Req, T]
NewStream creates a StreamAction.
func (*StreamAction[Req, T]) Use ¶
func (a *StreamAction[Req, T]) Use(h ...Hook[Req, iter.Seq2[T, error]]) *StreamAction[Req, T]
Use appends middleware to the setup phase.
type StreamHandler ¶
StreamHandler returns an iter.Seq2 that yields (item, error) pairs.
type SystemStats ¶
SystemStats is implemented lock-free by nexss/monitor.
type Testable ¶
type Testable[Req, Res any] struct { // contains filtered or unexported fields }
Testable wraps a BuiltAction and provides helpers for unit testing.
func TestFromAction ¶
func TestFromAction[Req, Res any](act *BuiltAction[Req, Res]) *Testable[Req, Res]
FromAction returns a Testable wrapper for a BuiltAction.
func (*Testable[Req, Res]) CaptureReq ¶
func (t *Testable[Req, Res]) CaptureReq(ctx context.Context, input Req) (captured Req, res Res, err error)
CaptureReq executes the action and captures the request as seen by the base handler. The captured value is the one after all middleware have run, right before calling exec.
type TypedPayload ¶
TypedPayload allows plugins (like OpenAPI) to discover the underlying Request and Response types at boot time without storing them in metadata or using reflection during execution.
Source Files
¶
- action.go
- admission.go
- async.go
- backoff.go
- builder.go
- builder_audit.go
- builder_auth.go
- builder_cache.go
- builder_dedup.go
- builder_event.go
- builder_guard.go
- builder_hooks.go
- builder_observe.go
- builder_quota.go
- builder_resilient.go
- builder_retry.go
- builder_rodo.go
- builder_tx.go
- builder_validation.go
- cache.go
- composer.go
- concurrency.go
- default_cache.go
- distributed.go
- doc.go
- execution.go
- fenced_lock.go
- history.go
- idempotency.go
- idempotency_default.go
- loadshed.go
- middleware_advanced.go
- middleware_timeout.go
- profile.go
- retry.go
- retry_predicate.go
- saga.go
- smart_resilience.go
- state_machine.go
- stream.go
- testable.go
- trace.go
- types.go