(new): expand runtime APIs
Golang lint / lint (push) Successful in 58s
Golang lint / lint (pull_request) Successful in 13m10s

(fix): harden concurrent lifecycle
(tests): add regression coverage
(doc): update v1.2 guidance
This commit is contained in:
2026-08-20 11:08:45 +03:00
parent 29b208eeec
commit 24040fe164
37 changed files with 1129 additions and 157 deletions
+14 -6
View File
@@ -10,6 +10,8 @@ import (
"git.scuroneko.dev/scuroneko/sneklog/v2"
)
var errMiddlewareBlocked = errors.New("middleware blocked call")
// Plugin represents a collection of commands and payloads (e.g., callback handlers),
// with shared middleware and configuration.
//
@@ -268,7 +270,7 @@ func (p *Plugin[T]) executeCmd(cmd string, ctx *MessageContext, db T) error {
// Run command-specific middlewares
for _, m := range command.middlewares {
if !m.Execute(ctx, db) {
return AsInternalError(errors.New("middleware blocked call"))
return errMiddlewareBlocked
}
}
@@ -289,7 +291,7 @@ func (p *Plugin[T]) executePayload(payload string, ctx *MessageContext, db T) er
// Run command-specific middlewares
for _, m := range command.middlewares {
if !m.Execute(ctx, db) {
return AsInternalError(errors.New("middleware blocked call"))
return errMiddlewareBlocked
}
}
@@ -363,6 +365,7 @@ func (m Middleware[T]) SetAsync(async bool) Middleware[T] {
// continue to share storage with the synchronous flow. Async middleware
// must treat those fields as read-only — mutating them races the sync chain
// that mutates the same context concurrently.
// Bot runtimes wait for tracked asynchronous middleware before returning.
func (m Middleware[T]) Execute(ctx *MessageContext, db T) bool {
if m.executor == nil {
reportMiddlewareError(ctx, m.name, ErrMiddlewareExecutorNil)
@@ -370,14 +373,19 @@ func (m Middleware[T]) Execute(ctx *MessageContext, db T) bool {
}
if m.async {
ctxCopy := *ctx
go func(ctx MessageContext) {
task := func() {
defer func() {
if recovered := recover(); recovered != nil {
reportMiddlewareError(&ctx, m.name, fmt.Errorf("%w in middleware %q: %v", ErrHandlerPanic, m.name, recovered))
reportMiddlewareError(&ctxCopy, m.name, fmt.Errorf("%w in middleware %q: %v", ErrHandlerPanic, m.name, recovered))
}
}()
m.executor(&ctx, db)
}(ctxCopy)
m.executor(&ctxCopy, db)
}
if ctx.asyncTask != nil {
ctx.asyncTask(task)
} else {
go task()
}
return true
}
result := false