Example 1
Example 1
import { createApplication } from '@setu-ts/kernel'; import { RuntimePlugin } from '@setu-ts/runtime'; import { WebSocketPlugin } from '@setu-ts/websocket-plugin'; import { CAPABILITIES, type IWebSocketService } from '@setu-ts/common'; const app = createApplication({ plugins: [RuntimePlugin(), WebSocketPlugin({ heartbeatMs: 30_000 })], }); const ws = app.services.get<IWebSocketService>(CAPABILITIES.WEBSOCKET); ws.route('/ws/chat', { onOpen: (conn, { query }) => { conn.data.set('room', query.room ?? 'lobby'); ws.room(query.room ?? 'lobby').add(conn); }, onMessage: (conn, data) => { ws.room(conn.data.get('room') as string).broadcast(data, { except: conn }); }, }); await app.start({ port: 3000 });
Sends heartbeats and closes idle connections on one shared interval.
-
isRunning(): boolean
Whether the interval is currently running.
-
start(): void
Starts the interval. A no-op when heartbeats are disabled, so a plugin left at its defaults never creates a timer.
-
stop(): void
Stops the interval. Idempotent.
-
tick(): void
Runs one sweep: closes connections that have been silent too long, then sends the heartbeat payload to the rest.
A named group of connections that can be addressed as one.
-
add(conn: IWebSocketConnection): void
Adds a connection to this room.
-
broadcast(): voiddata: string | Uint8Array,options?: RoomBroadcastOptions
Sends a frame to every open member, skipping any closed member and any member named by
options.except. -
broadcastJson<T>(): voidpayload: T,options?: RoomBroadcastOptions
Serializes a value to JSON once and broadcasts it as a text frame.
-
broadcastLocal(): voiddata: string | Uint8Array,options?: LocalBroadcastOptions
Sends a frame to this replica's own members only, without forwarding it to the backplane.
-
name(): string
The room name.
-
rawSize(): number
Total membership including connections that have since closed.
-
remove(conn: IWebSocketConnection): void
Removes a connection from this room.
-
size(): number
Number of currently open members.
Owns the set of live rooms, creating them on demand and dropping them once empty.
-
clear(): void
Discards every room.
-
deliverRemote(): voidname: string,data: string | Uint8Array,exceptId?: string
Delivers a frame that arrived from another replica to this replica's local members.
-
evict(conn: IWebSocketConnection): void
Removes a connection from every room it belongs to, then discards any room left empty.
-
get(name: string): Room
Returns the named room, creating it on first use.
-
peek(name: string): Room | undefined
Returns the named room if one already exists, without creating it.
-
size(): number
Number of live rooms.
A live WebSocket connection.
-
close(): voidcode?: number,reason?: string
Closes the connection. Idempotent.
-
data(): Map<string, unknown>
Per-connection application state, the socket-lifetime analogue of
IRequestContext.state. Use it to attach an authenticated user id, a tenant, or any value later handlers and broadcasts need. -
id(): string
Unique connection ID (from
runtime.uuid()). -
isOpen(): boolean
Whether the connection is still writable.
-
lastSeenAt(): number
The monotonic timestamp of the most recent inbound frame. Compared against another
runtime.hrtime()reading — never against a wall clock. -
markClosed(): void
Marks the connection closed without touching the transport — used when the peer closed first, so the socket is already gone.
-
participatesInHeartbeat(): boolean
Whether the shared heartbeat sweeper should include this connection. When
false, the sweeper skips both the payload send and idle eviction. -
path(): string
The path this connection was opened on.
-
readyState(): WebSocketReadyState
Current lifecycle state.
-
send(data: string | Uint8Array): void
Sends a frame to this peer.
-
sendJson<T>(payload: T): void
Serializes a value to JSON and sends it as a text frame.
-
touch(now: number): void
Records that a frame arrived, resetting the idle countdown.
The WebSocket hub.
-
available(): boolean
Whether the underlying HTTP adapter can perform WebSocket upgrades.
-
closeAll(): void
Closes every connection and stops the heartbeat. Called from the plugin's shutdown hook (AI_GUIDELINES §14.5).
-
connectionCount(): number
Current number of open connections across all routes.
-
createUpgradeRouter(): (request: Request) => Promise<WebSocketUpgradeDecision | null>
The router handed to the HTTP adapter. Matches the request against the route table, applies admission control, and builds the sink the adapter binds its native socket into.
-
deliverRemoteFrame(frame: RealtimeFrame): void
Delivers a frame that arrived from another replica to this replica's local room members.
-
peek(name: string): WebSocketRoom | undefined
Returns the named room if one already exists, without creating it.
-
replaceIngressBehaviors(behaviors: readonly IIngressBehavior[]): void
Replaces the plugin-level ingress chain around
onMessagewith the resolved declared sequence. -
room(name: string): WebSocketRoom
Returns the named room, creating it on first use.
-
roomCount(): number
Current number of live rooms.
-
route(): voidpath: string,handlers: WebSocketHandlers,options?: WebSocketRouteOptions
Registers a WebSocket route. Paths match exactly; the query string is ignored for matching and exposed to
onOpeninstead. -
routeCount(): number
Number of registered routes — reported by the health indicator.
-
routeUpgrade(): Promise<WebSocketUpgradeDecision | null>request: Request,principal?: IPrincipal
The upgrade router the kernel terminal handler consults after the middleware pipeline has run without short-circuiting.
The registered WebSocket routes.
-
add(): voidpath: string,handlers: WebSocketHandlers,options?: WebSocketRouteOptions
Registers a route.
-
match(request: Request): WsRouteMatch | null
Matches an upgrade request.
-
size(): number
Number of registered routes.
Builds the context handed to onOpen from the upgrade request.
Measures an inbound frame in bytes.
Parses a Sec-WebSocket-Protocol header into its comma-separated tokens.
Applies defaults and rejects a configuration that cannot work.
Selects the subprotocol to echo for a route.
Creates the WebSocketPlugin.
Configuration for the sweeper.
-
heartbeatMs: number
Tick interval in milliseconds;
0disables the sweeper entirely. -
heartbeatPayload: string
The text frame sent on each tick.
-
idleTimeoutMs: number
Inbound silence in milliseconds after which a connection is closed;
0disables.
A live WebSocket connection, as seen by application code.
-
close(): voidcode?: number,reason?: string
Closes the connection. Idempotent.
-
data: Map<string, unknown>
Per-connection application state, the socket-lifetime analogue of
IRequestContext.state. Use it to attach an authenticated user id, a tenant, or any value later handlers and broadcasts need. -
id: string
Unique connection ID (from
runtime.uuid()). -
isOpen: boolean
Whether the connection is still writable.
-
path: string
The path this connection was opened on.
-
readyState: WebSocketReadyState
Current lifecycle state.
-
send(data: string | Uint8Array): void
Sends a frame to this peer.
-
sendJson<T>(payload: T): void
Serializes a value to JSON and sends it as a text frame.
Service contract for the WebSocket hub — registered by the WebSocketPlugin
under CAPABILITIES.WEBSOCKET.
-
available: boolean
Whether the underlying HTTP adapter can perform WebSocket upgrades.
-
connectionCount: number
Current number of open connections across all routes.
-
peek(name: string): WebSocketRoom | undefined
Returns the named room if one already exists, without creating it.
-
room(name: string): WebSocketRoom
Returns the named room, creating it on first use.
-
roomCount: number
Current number of live rooms.
-
route(): voidpath: string,handlers: WebSocketHandlers,options?: WebSocketRouteOptions
Registers a WebSocket route. Paths match exactly; the query string is ignored for matching and exposed to
onOpeninstead. -
routeUpgrade(): Promise<WebSocketUpgradeDecision | null>request: Request,principal?: IPrincipal
Consults the internal upgrade router for an inbound request. Used by the kernel terminal handler to decide whether to upgrade after the middleware pipeline runs.
The runtime-native socket, normalized to the two operations the framework
needs. Implemented by each HTTP adapter's upgrader over its platform socket
(Deno.upgradeWebSocket's WebSocket, a ws socket on Node, Bun's
ServerWebSocket, the server half of a Workers WebSocketPair).
-
close(): voidcode?: number,reason?: string
Closes the socket.
-
readyState: WebSocketReadyState
Current lifecycle state of the underlying socket.
-
send(data: string | Uint8Array): void
Sends a frame to the peer. A
stringis sent as a text frame, aUint8Arrayas a binary frame.
Options for Room.broadcastLocal.
-
exceptId: string
Skip the member with this connection ID.
Options for a room broadcast.
-
except: IWebSocketConnection
A member to skip — typically the sender, so it does not echo to itself.
Notified whenever a connection joins or leaves a Room.
-
onJoin(conn: IWebSocketConnection): void
Called when a connection is added to a room it was not already in.
-
onLeave(conn: IWebSocketConnection): void
Called when a connection is removed from a room it was in — whether by an explicit
Room.removeor by being dropped mid-broadcast.
Payload of a WebSocket close, normalized across runtimes.
-
code: number
The RFC 6455 close code (e.g.
1000normal,1001going away). -
reason: string
The close reason; an empty string when the peer supplied none.
Details of the upgrade request that opened a connection, handed to
WebSocketHandlers.onOpen.
-
headers: Headers
The upgrade request headers — read these to authenticate the peer.
-
path: string
The URL path component (no query string).
-
protocol: string
The negotiated subprotocol, when one was selected.
-
query: Readonly<Record<string, string>>
Query string parameters.
-
url: string
The full upgrade request URL.
-
user: IPrincipal
The authenticated principal, when one authenticated the upgrade. Populated by threading
ctx.request.userthroughIWebSocketService.routeUpgrade; omitted when the upgrade was not authenticated. Read this inonOpento identify the peer rather than re-deriving it from the headers.
The callbacks an HTTP adapter drives once it has completed a handshake. The WebSocket plugin builds one sink per accepted upgrade and hands it to the adapter inside the accept decision; the adapter binds its native socket events to these methods.
-
onClose(event: WebSocketCloseEvent): void
Called once, when the socket closes for any reason.
-
onError(error: Error): void
Called when the socket reports a transport-level error. A socket that errors is also expected to close, so implementations must tolerate
WebSocketEventSink.onClosearriving afterwards. -
onMessage(data: string | Uint8Array): void
Called for every inbound frame.
-
onOpen(transport: IWebSocketTransport): void
Called once, when the socket is live and writable.
The lifecycle callbacks an application supplies per WebSocket route.
-
onClose(): void | Promise<void>conn: IWebSocketConnection,event: WebSocketCloseEvent
Called once, when the connection closes for any reason.
-
onError(): void | Promise<void>conn: IWebSocketConnection,error: Error
Called on a transport error, and on a rejected promise from any other callback.
-
onMessage(): void | Promise<void>conn: IWebSocketConnection,data: string | Uint8Array
Called for every inbound frame.
-
onOpen(): void | Promise<void>conn: IWebSocketConnection,context: WebSocketConnectionContext
Called once per connection, after the handshake completes.
Configuration for WebSocketPlugin.
-
behaviors: readonly (IIngressBehavior | RegistryFactory<IIngressBehavior>)[]
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 (IIngressBehaviorin@setu-ts/common). -
heartbeatMs: number
Interval in milliseconds at which
WebSocketPluginOptions.heartbeatPayloadis sent to every open connection.0(the default) disables the heartbeat entirely and creates no timer. -
heartbeatPayload: string
The text frame sent on each heartbeat tick. Defaults to
'ping'. Read only whenWebSocketPluginOptions.heartbeatMsis above0. -
idleTimeoutMs: number
Milliseconds of inbound silence after which a connection is closed with code
1001.0(the default) disables idle closing. -
maxConnections: number
Maximum number of simultaneously open connections across all routes.
0(the default) means unlimited. At the limit, further upgrade requests are refused with HTTP 503 before any socket is created. -
maxMessageBytes: number
Maximum size in bytes of a single inbound frame.
0(the default) means unlimited. A larger frame closes the connection with code1009(message too big) and is never delivered toonMessage. -
routes: readonly WebSocketRouteEntry[]
Routes registered declaratively, as an alternative to calling
service.route(...)imperatively afterstart(). Each entry — instance orRegistryFactory— produces oneroute()call, so a route can be declared where the plugin is composed instead of after the application has started. -
scalingNotice: boolean
Whether to log one
infoline at registration when no realtime backplane is registered, stating that rooms broadcast in-process only. Defaults totrue.
A named broadcast group of connections — the bidirectional analogue of the SSE plugin's channels.
-
add(conn: IWebSocketConnection): void
Adds a connection to this room.
-
broadcast(): voiddata: string | Uint8Array,options?: RoomBroadcastOptions
Sends a frame to every open member, skipping any closed member and any member named by
options.except. -
broadcastJson<T>(): voidpayload: T,options?: RoomBroadcastOptions
Serializes a value to JSON once and broadcasts it as a text frame.
-
name: string
The room name.
-
remove(conn: IWebSocketConnection): void
Removes a connection from this room.
-
size: number
Number of currently open members.
The declarative form of one IWebSocketService.route() call — the entry
an application writes instead of calling route() imperatively after
start().
-
handlers: WebSocketHandlers
The lifecycle callbacks, exactly as the imperative
route()accepts. -
options: WebSocketRouteOptions
Per-route configuration, including the route's upgrade guards.
-
path: string
The exact URL path to accept upgrades on (e.g.
/ws/chat).
Per-route configuration supplied alongside the handlers.
-
guards: readonly WebSocketUpgradeGuard[]
Guards evaluated before this route's WebSocket handshake is accepted.
-
heartbeat: boolean
Whether this route participates in the shared heartbeat sweep.
-
protocols: readonly string[]
Subprotocols this route accepts. When non-empty, the first client-requested protocol appearing in this list is echoed back and any request whose
Sec-WebSocket-Protocolmatches none of them is rejected with 400. When omitted, no protocol is negotiated and none is echoed.
One registered WebSocket route.
-
handlers: WebSocketHandlers
The application's lifecycle callbacks.
-
heartbeat: boolean
Whether this route participates in the shared heartbeat sweep. Defaults to
trueso existing routes are unaffected. -
path: string
The exact path this route serves.
-
protocols: readonly string[]
Subprotocols this route accepts, empty when none are configured.
Forwards a local broadcast to peers on other replicas.
Lifecycle state of a WebSocket, normalized across runtimes to names rather than the numeric codes the web API uses.
| RegistryFactory<WebSocketRouteDefinition>
One entry of WebSocketPluginOptions.routes: a route definition,
or a RegistryFactory producing one when the handlers need a
resolved capability.
| { readonly accept: false; readonly status: number; }
What an HTTP adapter should do with an inbound upgrade request, as decided
by the WebSocketUpgradeRouter.
Consulted by an HTTP adapter for every inbound WebSocket upgrade request.
| { readonly matched: false; readonly status: number; }
The outcome of matching an upgrade request against the table.
Standard capability tokens provided by the first-party plugins.
-
AUDIT: string
Audit trail logging.
-
AUTH: string
Authentication service.
-
AUTHORIZATION: string
Authorization service (RBAC, permissions).
-
CACHE: string
Key/value caching.
-
CLI_COMMAND: string
CLI command contributions (multi-provider).
-
CLOUDFLARE: string
Cloudflare Workers platform bindings (KV, R2, D1, Queues, service and Durable Object namespaces) published as one typed accessor.
-
COMMAND_BUS: string
Command bus (CQRS).
-
CONFIG: string
Configuration access.
-
CQRS: string
CQRS facade.
-
DATABASE: string
Database access (repositories, unit of work).
-
DECORATOR_HANDLER: string
Decorator handler contributions (multi-provider).
-
DI_CONTAINER: string
Optional dependency injection container.
-
EVENTS: string
In-memory domain event bus.
-
FEATURE_FLAGS: string
Feature flag evaluation.
-
GRAPHQL: string
GraphQL plugin — schema-first and code-first GraphQL-over-HTTP.
-
GRPC: string
gRPC plugin — server-side Connect/gRPC/gRPC-Web co-serving.
-
HEALTH: string
Health checks.
-
HEALTH_INDICATOR: string
Health indicator contributions (multi-provider).
-
HTTP_ADAPTER: string
HTTP server adapter — the runtime plugin registers its IHttpAdapter here.
-
JWT: string
JWT sign/verify service.
-
LOGGER: string
Structured logger.
-
MAIL: string
Email sending.
-
MESSAGING: string
Message broker for integration events.
-
METADATA_STORE: string
Decorator metadata store (from the DecoratorPlugin, when registered).
-
METRICS: string
Metrics collection.
-
METRIC_REGISTRATION: string
Metric registration contributions (multi-provider).
-
MULTI_TENANCY: string
Multi-tenancy service.
-
NOTIFICATION: string
Multi-channel notifications.
-
OPENAPI: string
OpenAPI spec contribution and generation.
-
OPENAPI_SCHEMA: string
OpenAPI schema contributions (multi-provider).
-
QUERY_BUS: string
Query bus (CQRS).
-
QUEUE: string
Background job queue.
-
REALTIME_BACKPLANE: string
Pub/sub transport carrying real-time broadcasts between application instances, so WebSocket rooms and SSE channels fan out across replicas. Consumed optionally — absent means purely in-process broadcasting.
-
RESILIENCE: string
Resilience patterns (circuit breaker, retry, timeout, bulkhead).
-
RUNTIME: string
Runtime services provided by the RuntimePlugin. Mandatory in every application.
-
SCHEDULER: string
Job scheduling (cron, delayed, recurring).
-
SECRETS: string
Secret management.
-
SERVICE_DISCOVERY: string
Service discovery — logical service name to reachable instances.
-
SESSION: string
Cookie-backed sessions for server-rendered applications.
-
SSE: string
Server-Sent Events (SSE) hub for in-process real-time broadcasting.
-
SSR: string
Server-side rendering (SSR) — React Router or similar framework.
-
STATIC_FILES: string
Static file serving plugin.
-
STORAGE: string
File storage.
-
TELEMETRY: string
Distributed tracing.
-
VALIDATION: string
Request/data validation.
-
VIEW: string
View rendering (server-rendered HTML) — an
IViewEnginethat turns a view component and its props into an HTML string, so a handler can answer with markup it did not concatenate by hand. -
WEBSOCKET: string
WebSocket hub for bidirectional real-time messaging.
-
WORKER_POOL: string
Worker-thread pool for CPU-bound tasks.
Usage
import * as WebSocket_plugin_for_full_duplex__bidirectional_real_time_messaging___completing_the_real_time_story_that_the_SSE_plugin__Milestone_43__covers_one_way___Routes_are_declared_with_lifecycle_handlers__connections_are_addressed_individually_or_through_named_rooms__and_the_RFC_6455_handshake_is_performed_by_the_runtime_s_HTTP_adapter__so_the_same_application_code_runs_on_Node__Deno__Bun__and_Cloudflare_Workers__ from "websocket-plugin/src/index.ts";