Plugin-level ingress behaviours wrapped around every route's onMessage
— the WebSocket arm of the transport-neutral behaviour chain shared with
the queue, scheduler, and messaging plugins (IIngressBehavior in
@setu-ts/common).
Each behaviour observes an IngressContext carrying kind: 'websocket',
the route path as name, and the frame as payload, and runs in declared
order ahead of the handler. A behaviour that returns without calling
next() short-circuits: the handler never sees the frame. A behaviour
that throws is routed to the route's onError, exactly as a failing
handler is. Behaviours are plugin-level by design; there is no
route-level behaviors arm (guards are the per-route mechanism).
Configuring a behaviour makes the dispatch result promise-mediated while
preserving immediate execution for entirely synchronous behaviours. A
behaviour that defers next() delays the wrapped handler. With no
behaviours configured, dispatch is byte-identical to the pre-chain
behaviour: a direct, synchronous invoke.
No startup gate is needed here, unlike the queue, scheduler, and
messaging arms: a frame cannot arrive before its socket is open, and the
application does not serve until after onInit has resolved the chain.
Instance entries are handed to the service at register(); factory
entries are resolved in the onInit phase and a throwing factory rejects
start() naming WebSocketPlugin({ behaviors }) and the entry's index
in THIS declared array.