feat: three-tier context lifecycle (grace → suspended → reaped)
All checks were successful
CI / Build and Test (push) Successful in 1m22s
All checks were successful
CI / Build and Test (push) Successful in 1m22s
Contexts that lose their SSE connection now pass through a suspended state before being fully reaped. Suspended contexts keep their shell (ID, route, CSRF token) but free page resources. On reconnect, the page init function is re-run for a seamless resume. Contexts past the TTL trigger a client-side reload instead of a silent dead page. Configurable via ContextSuspendAfter (default 15m) and ContextTTL (default 1h).
This commit is contained in:
@@ -42,6 +42,7 @@ type Context struct {
|
||||
createdAt time.Time
|
||||
sseConnected atomic.Bool
|
||||
sseDisconnectedAt atomic.Pointer[time.Time]
|
||||
suspended atomic.Bool
|
||||
}
|
||||
|
||||
// View defines the UI rendered by this context.
|
||||
@@ -400,6 +401,13 @@ func (c *Context) resetPageState() {
|
||||
c.mu.Unlock()
|
||||
}
|
||||
|
||||
// suspend frees page-scoped resources while keeping the context shell alive
|
||||
// in the registry for seamless re-init on reconnect.
|
||||
func (c *Context) suspend() {
|
||||
c.resetPageState()
|
||||
c.suspended.Store(true)
|
||||
}
|
||||
|
||||
// Navigate performs an SPA navigation to the given path. It resets page state,
|
||||
// runs the target page's init function (with middleware), and pushes the new
|
||||
// view over the existing SSE connection with a view transition animation.
|
||||
|
||||
Reference in New Issue
Block a user