nest_rs_ws/lib.rs
1//! WebSocket gateways for nestrs.
2//!
3//! A `#[gateway]` struct with a `#[messages]` impl holds
4//! `#[subscribe_message("event")]` handlers. Messages ride a JSON envelope
5//! `{ "event": "...", "data": ... }`. Because a WS upgrade is an HTTP `GET`,
6//! a gateway self-mounts on the existing HTTP transport — listing it in
7//! `#[module(providers = [...])]` is the entire wiring; it inherits port,
8//! CORS, TLS, and is governed by the boot-time access graph.
9//!
10//! ```ignore
11//! #[gateway(path = "/ws")]
12//! #[use_guards(AuthGuard)]
13//! struct ChatGateway {
14//! #[inject] svc: Arc<RoomService>,
15//! }
16//!
17//! #[messages]
18//! impl ChatGateway {
19//! #[subscribe_message("message")]
20//! async fn on_message(&self, msg: SendMessage) -> ChatMessage { /* ... */ }
21//! }
22//! ```
23//!
24//! # Return-type contract
25//!
26//! - `()` — send nothing.
27//! - `T` — serialize as the reply on the request's event name.
28//! - `Result<(), E>` / `Result<T, E>` — `Err(e)` becomes an error frame
29//! `{ "event": "<event>", "data": { "error": "<Display of e>" } }` and a
30//! `warn!(target: "nest_rs::ws", ...)` log.
31//!
32//! Detection is syntactic on the type's last path segment being `Result`: a
33//! type alias over `Result` is **not** detected and would leak the error
34//! variant on the wire. Always return `Result` (or `std::result::Result`)
35//! directly. `Display` for the error must be wire-safe — avoid
36//! `#[error(transparent)]` over an ORM/sqlx error.
37//!
38//! # Server→client push
39//!
40//! [`WsServer`] is the `@WebSocketServer` analog — a connection registry
41//! provided by [`WsModule`]. A handler reaches it by declaring a
42//! `&`[`WsClient`] parameter (a reference, distinguished from the owned
43//! payload). Pushes funnel through a per-connection outbox drained by a
44//! writer task, so the read loop never blocks on a slow `Sink`.
45//!
46//! # Guards and lifecycle hooks
47//!
48//! - **Connection-level**: `#[use_guards]` on the gateway struct reuses the
49//! HTTP `Guard` trait and runs on the upgrade request.
50//! - **Per-message**: `#[use_guards]` beside a `#[subscribe_message]` runs
51//! the Layer System chain (global + per-message, deduped by `TypeId`)
52//! each time the event fires — same `Guard::check_ws_message` interface.
53//!
54//! `#[on_connect]` / `#[on_disconnect]` on the `#[messages]` impl block are
55//! the `OnGatewayConnection` / `OnGatewayDisconnect` analogs; `on_disconnect`
56//! runs while the connection is still registered.
57//!
58//! # Per-gateway namespacing
59//!
60//! [`WsServer`] is generic over a zero-sized namespace marker (default
61//! [`Global`]). `#[gateway(namespace = MyNs)]` mounts against its own
62//! `WsServer<MyNs>` — a separate registry the macro self-provides — so two
63//! gateways isolate without sharing a registry.
64//!
65//! # Ambient request data context
66//!
67//! The connection loop runs in a task *after* the upgrade completes, so the
68//! task-locals an HTTP request installs have unwound by the time a message
69//! handler runs. The [`SocketContext`] seam captures opaque per-connection
70//! state from the post-guard upgrade request and re-installs it around each
71//! dispatch — this is how `nest_rs_seaorm::ws` re-binds executor + ability
72//! per message without `nestrs-ws` depending on the ORM or authz.
73
74mod context;
75mod envelope;
76mod gateway;
77mod guard;
78mod module;
79mod server;
80
81pub use context::{BoxFuture, Captured, SocketContext};
82pub use envelope::{WsEnvelope, WsReply};
83pub use gateway::{Gateway, GatewayEndpoint, gateway_endpoint};
84pub use guard::{EventLayerTable, WsMessageCheck};
85pub use module::WsModule;
86pub use server::{ConnId, Global, Registry, WsClient, WsServer};
87
88// Re-exported so macro-generated code resolves these through the framework.
89pub use async_trait::async_trait;
90pub use serde_json;
91pub use tracing;
92
93pub use poem;
94
95pub use nest_rs_ws_macros::{gateway, messages};