@super-line/server 0.2.0 → 0.4.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/dist/index.d.cts CHANGED
@@ -1,25 +1,16 @@
1
- import { Server, IncomingMessage } from 'node:http';
2
- import { ServerMessageDef, Serializer, ServerFrame, EmitData, ConnDescriptor, Adapter, PresenceStore, Contract, RoleOf, NodeStat, SharedEvents, SharedServerRequests, ClientInput, Output, SharedRequests, ServerInput, AnyData, RoleRequests, Events, DataOf, RoleTopics, SharedTopics, ServerEvents, ServerEmit, ServerData } from '@super-line/core';
3
- import { WebSocket } from 'ws';
1
+ import { ServerMessageDef, RawConn, Serializer, ServerFrame, EmitData, ConnDescriptor, Adapter, PresenceStore, Contract, RoleOf, NodeStat, SharedEvents, SharedServerRequests, ClientInput, Output, SharedRequests, ServerInput, AnyData, RoleRequests, Events, DataOf, RoleTopics, SharedTopics, EventData, ServerTransport, Handshake } from '@super-line/core';
4
2
 
5
- /** Backpressure policy: what to do when a connection's send buffer grows too large. */
6
- interface Backpressure {
7
- /** Buffer size (bytes) above which {@link Backpressure.onExceed} kicks in. */
8
- maxBufferedBytes: number;
9
- /** `'close'` (default) drops the connection with code 1013; `'drop'` skips the frame. */
10
- onExceed?: 'close' | 'drop';
11
- }
12
3
  /**
13
4
  * A single client connection, passed to handlers as the third argument.
14
5
  *
15
- * Node-local: `conn` objects live on the node that accepted the upgrade, so don't
6
+ * Node-local: `conn` objects live on the node that accepted the connection, so don't
16
7
  * stash one to reach a user later — cross-node delivery goes through the Adapter
17
8
  * (use a per-user room instead). Generic over the events it may emit (scoped by
18
9
  * role), its `ctx`, and its `role`.
19
10
  */
20
11
  declare class Conn<Ev = Record<string, ServerMessageDef>, Ctx = unknown, Role extends string = string, Data = unknown> {
21
- /** The underlying `ws` socket. `conn.ws.terminate()` simulates a drop in tests. */
22
- readonly ws: WebSocket;
12
+ /** The underlying transport connection. `conn.terminate()` simulates a drop in tests. */
13
+ readonly raw: RawConn;
23
14
  /** Server-assigned unique id for this connection (stable for its lifetime). */
24
15
  readonly id: string;
25
16
  /** This connection's role (the literal resolved by `authenticate`). */
@@ -27,12 +18,15 @@ declare class Conn<Ev = Record<string, ServerMessageDef>, Ctx = unknown, Role ex
27
18
  /** The context `authenticate` returned for this connection. */
28
19
  readonly ctx: Ctx;
29
20
  private readonly serializer;
30
- private readonly backpressure?;
21
+ /** Optional inspector tap: called with each `emit` so the server can mirror it to inspectors. */
22
+ private readonly onEmit?;
31
23
  /** Namespaced channels (rooms + topics) this connection belongs to. */
32
24
  readonly channels: Set<string>;
33
25
  /** Mutable per-connection scratch state, typed per role by the contract's `data` schema. */
34
26
  data: Data;
35
- /** When this connection was accepted (`Date.now()` at the upgrade). */
27
+ /** The client↔server transport (wire) this connection was accepted on (set by the server at accept). */
28
+ transport?: string;
29
+ /** When this connection was accepted (`Date.now()`). */
36
30
  readonly connectedAt: number;
37
31
  /** When the server last sent a heartbeat ping to this connection (managed by the server). */
38
32
  lastPingAt?: number;
@@ -41,23 +35,26 @@ declare class Conn<Ev = Record<string, ServerMessageDef>, Ctx = unknown, Role ex
41
35
  /** Pings sent since the last pong; drives reaping (managed by the server). */
42
36
  missedPongs: number;
43
37
  constructor(
44
- /** The underlying `ws` socket. `conn.ws.terminate()` simulates a drop in tests. */
45
- ws: WebSocket,
38
+ /** The underlying transport connection. `conn.terminate()` simulates a drop in tests. */
39
+ raw: RawConn,
46
40
  /** Server-assigned unique id for this connection (stable for its lifetime). */
47
41
  id: string,
48
42
  /** This connection's role (the literal resolved by `authenticate`). */
49
43
  role: Role,
50
44
  /** The context `authenticate` returned for this connection. */
51
- ctx: Ctx, serializer: Serializer, backpressure?: Backpressure | undefined);
52
- private overBackpressure;
45
+ ctx: Ctx, serializer: Serializer,
46
+ /** Optional inspector tap: called with each `emit` so the server can mirror it to inspectors. */
47
+ onEmit?: ((event: string, data: unknown) => void) | undefined);
53
48
  /** Encode and send a frame (unicast, e.g. req/res). */
54
49
  send(frame: ServerFrame): void;
55
50
  /** Forward an already-encoded frame (fan-out path; encoded once at the source). */
56
51
  sendRaw(payload: string | Uint8Array): void;
57
52
  /** Push an event to THIS connection (node-local). Scoped to the role's events. */
58
53
  emit<E extends keyof Ev>(event: E, data: EmitData<Ev[E]>): void;
59
- /** Close the underlying socket. */
54
+ /** Graceful close of the underlying transport connection. */
60
55
  close(): void;
56
+ /** Hard close with no handshake — used by heartbeat reaping. */
57
+ terminate(): void;
61
58
  }
62
59
 
63
60
  /**
@@ -127,12 +124,17 @@ type Handlers<C extends Contract, A> = ([keyof SharedRequests<C>] extends [never
127
124
  };
128
125
  /** Context passed to middleware and lifecycle hooks about the current operation. */
129
126
  interface MiddlewareInfo {
130
- /** Whether this is a request or a topic subscribe. */
131
- kind: 'request' | 'subscribe';
132
- /** The request/topic name. */
127
+ /** Whether this is a request, a topic subscribe, or a bus event delivery. */
128
+ kind: 'request' | 'subscribe' | 'event';
129
+ /** The request/topic/event name. */
133
130
  name: string;
134
- /** The connection the operation is on (`conn.role` available). */
135
- conn: Conn;
131
+ /** The connection the operation is on, if any (`conn.role` available). Absent for bus events. */
132
+ conn?: Conn;
133
+ }
134
+ /** Metadata passed to a {@link SuperLineServer.subscribe} callback alongside the event payload. */
135
+ interface BusMeta {
136
+ /** The node that published the event. Equals `srv.nodeId` for a same-node publish (local echo). */
137
+ from: string;
136
138
  }
137
139
  /**
138
140
  * Flat middleware run before request/subscribe handlers. Call `next()` to proceed,
@@ -186,7 +188,7 @@ interface ConnTarget<C extends Contract> {
186
188
  emit<E extends keyof SharedEvents<C>>(event: E, data: EmitData<SharedEvents<C>[E]>): void;
187
189
  /**
188
190
  * Send a shared server→client request and await the client's typed reply
189
- * (cross-node). Rejects with a `TIMEOUT` `SocketError` if no live node owns
191
+ * (cross-node). Rejects with a `TIMEOUT` `SuperLineError` if no live node owns
190
192
  * the connection or the client doesn't answer in time.
191
193
  */
192
194
  request<M extends keyof SharedServerRequests<C>>(name: M, input: ClientInput<SharedServerRequests<C>[M]>, opts?: {
@@ -208,18 +210,21 @@ interface RoleLens<C extends Contract, R extends RoleOf<C>> {
208
210
  /** Publish to a topic in role `R`'s surface (reaches that role's subscribers). */
209
211
  publish<T extends keyof RoleTopics<C, R>>(topic: T, data: EmitData<RoleTopics<C, R>[T]>): void;
210
212
  }
211
- /** Options for {@link createSocketServer}. */
212
- interface ServerOptions<C extends Contract, A extends AuthResult<C>> {
213
- /** The `http.Server` to attach to (compose with Express/Fastify/Hono). */
214
- server?: Server;
213
+ /** Options for {@link createSuperLineServer}. */
214
+ interface SuperLineServerOptions<C extends Contract, A extends AuthResult<C>> {
215
+ /** Client↔server transports to accept connections on (e.g. `webSocketServerTransport({ server })`). */
216
+ transports: ServerTransport[];
215
217
  /** Wire serializer; MUST match the client. Defaults to `jsonSerializer`. */
216
218
  serializer?: Serializer;
217
219
  /** Cross-node fan-out adapter. Defaults to a per-server in-memory adapter. */
218
220
  adapter?: Adapter;
219
- /** Only handle upgrades for this pathname; others are left untouched. */
220
- path?: string;
221
- /** Runs at the HTTP upgrade. Return { role, ctx }, or throw to reject with 401. */
222
- authenticate: (req: IncomingMessage) => Awaitable<A>;
221
+ /**
222
+ * Friendly name for this node, surfaced in `srv.nodeName`, the cluster descriptor, and the
223
+ * Control Center topology. Defaults to `SUPER_LINE_NODE_NAME` or a short slice of `nodeId`.
224
+ */
225
+ nodeName?: string;
226
+ /** Authenticate a connection from its normalized {@link Handshake}. Return { role, ctx }, or throw to reject. */
227
+ authenticate: (handshake: Handshake) => Awaitable<A>;
223
228
  /** Stable user key for a connection (powers `cluster.byUser`, `isOnline`, and `toUser`). */
224
229
  identify?: (conn: Conn) => string | undefined;
225
230
  /** Extra fields merged into the connection's cluster descriptor (e.g. `{ plan }`). `ctx` is never auto-serialized. */
@@ -238,8 +243,14 @@ interface ServerOptions<C extends Contract, A extends AuthResult<C>> {
238
243
  interval?: number;
239
244
  maxMissed?: number;
240
245
  } | false;
241
- /** Guard against slow consumers: when a connection's send buffer exceeds the limit, close or drop. */
242
- backpressure?: Backpressure;
246
+ /**
247
+ * Enable the read-only Control Center inspector: emit `msg.*` telemetry and accept inspector
248
+ * clients. The WS transport must also be created with `inspector: true` to negotiate the
249
+ * `superline.inspector.v1` subprotocol. **Default off; dev / trusted-network only.**
250
+ */
251
+ inspector?: boolean | {
252
+ redact?: string[];
253
+ };
243
254
  /** Called once per accepted connection. */
244
255
  onConnection?: (conn: Conn, ctx: CtxUnion<A>) => void;
245
256
  /** Called when a connection closes, with the WebSocket close `code`. */
@@ -247,10 +258,12 @@ interface ServerOptions<C extends Contract, A extends AuthResult<C>> {
247
258
  /** Called for any error thrown in middleware/handlers (after the client is replied to). */
248
259
  onError?: (error: unknown, info: MiddlewareInfo) => void;
249
260
  }
250
- /** A running super-line server, returned by {@link createSocketServer}. */
251
- interface SocketServer<C extends Contract, A extends AuthResult<C>> {
261
+ /** A running super-line server, returned by {@link createSuperLineServer}. */
262
+ interface SuperLineServer<C extends Contract, A extends AuthResult<C>> {
252
263
  /** This node's stable id (unique per server process). */
253
264
  readonly nodeId: string;
265
+ /** This node's friendly name (from `nodeName`/`SUPER_LINE_NODE_NAME`, else a short `nodeId` slice). */
266
+ readonly nodeName: string;
254
267
  /** Synchronous, node-local introspection (connections, rooms, topics on this process). */
255
268
  readonly local: LocalView;
256
269
  /** Asynchronous, cluster-wide introspection backed by the adapter's presence directory. */
@@ -262,34 +275,37 @@ interface SocketServer<C extends Contract, A extends AuthResult<C>> {
262
275
  /** Target all of a user's connections (by `identify` key) across nodes (emit/disconnect). */
263
276
  toUser(userId: string): UserTarget<C>;
264
277
  /** Register handlers for shared + per-role requests (chainable). */
265
- implement(handlers: Handlers<C, A>): SocketServer<C, A>;
278
+ implement(handlers: Handlers<C, A>): SuperLineServer<C, A>;
266
279
  /** Mixed-role connection group; broadcast() sends a shared contract event to members. */
267
280
  room(name: string): Room<C>;
268
281
  /** Publish a SHARED topic to all subscribers (server-only publish). */
269
282
  publish<T extends keyof SharedTopics<C>>(topic: T, data: EmitData<SharedTopics<C>[T]>): void;
283
+ /**
284
+ * Subscribe SERVER-side to a shared topic, cluster-wide. The callback fires for a
285
+ * publish from any node — including this one (local echo, delivered in-process with
286
+ * no round-trip). `meta.from` is the publishing node; self-exclude with
287
+ * `if (meta.from === srv.nodeId) return`. Returns an unsubscribe fn.
288
+ */
289
+ subscribe<T extends keyof SharedTopics<C>>(topic: T, handler: (data: EventData<SharedTopics<C>[T]>, meta: BusMeta) => void): () => void;
270
290
  /** Lens for role-scoped sends, e.g. forRole('user').publish('feed', data). */
271
291
  forRole<R extends RoleOf<C>>(role: R): RoleLens<C, R>;
272
- /** Broadcast a typed event to all OTHER server nodes (at-most-once, excludes self). */
273
- emitServer<E extends keyof ServerEvents<C>>(event: E, data: ServerEmit<ServerEvents<C>[E]>): void;
274
- /** Listen for inter-server events from other nodes. Returns an unsubscribe fn. */
275
- onServer<E extends keyof ServerEvents<C>>(event: E, handler: (data: ServerData<ServerEvents<C>[E]>) => void): () => void;
276
292
  close(): Promise<void>;
277
293
  }
278
294
  /**
279
295
  * Create a server bound to a contract. Attach it to an `http.Server`, then call
280
- * {@link SocketServer.implement} with your handlers. `authenticate` resolves each
296
+ * {@link SuperLineServer.implement} with your handlers. `authenticate` resolves each
281
297
  * connection's `{ role, ctx }` at the upgrade.
282
298
  *
283
299
  * @param contract - the shared contract.
284
300
  * @param opts - server options; `authenticate` is required.
285
- * @returns the {@link SocketServer}.
286
- * @throws nothing directly; handler throws become a typed `SocketError` to the client.
301
+ * @returns the {@link SuperLineServer}.
302
+ * @throws nothing directly; handler throws become a typed `SuperLineError` to the client.
287
303
  *
288
304
  * @example
289
305
  * ```ts
290
- * const srv = createSocketServer(api, {
291
- * server,
292
- * authenticate: (req) => ({ role: 'user' as const, ctx: { id: '1' } }),
306
+ * const srv = createSuperLineServer(api, {
307
+ * transports: [webSocketServerTransport({ server })],
308
+ * authenticate: (h) => ({ role: 'user' as const, ctx: { id: '1' } }),
293
309
  * })
294
310
  * srv.implement({
295
311
  * shared: { join: async ({ room }, _ctx, conn) => { srv.room(room).add(conn); return { ok: true } } },
@@ -297,6 +313,6 @@ interface SocketServer<C extends Contract, A extends AuthResult<C>> {
297
313
  * })
298
314
  * ```
299
315
  */
300
- declare function createSocketServer<C extends Contract, A extends AuthResult<C>>(contract: C, opts: ServerOptions<C, A>): SocketServer<C, A>;
316
+ declare function createSuperLineServer<C extends Contract, A extends AuthResult<C>>(contract: C, opts: SuperLineServerOptions<C, A>): SuperLineServer<C, A>;
301
317
 
302
- export { type AuthResult, type Backpressure, type ClusterView, Conn, type ConnTarget, type Handlers, type LocalView, MemoryBus, type Middleware, type MiddlewareInfo, type RoleLens, type Room, type ServerOptions, type SocketServer, type UserTarget, createInMemoryAdapter, createSocketServer };
318
+ export { type AuthResult, type BusMeta, type ClusterView, Conn, type ConnTarget, type Handlers, type LocalView, MemoryBus, type Middleware, type MiddlewareInfo, type RoleLens, type Room, type SuperLineServer, type SuperLineServerOptions, type UserTarget, createInMemoryAdapter, createSuperLineServer };
package/dist/index.d.ts CHANGED
@@ -1,25 +1,16 @@
1
- import { Server, IncomingMessage } from 'node:http';
2
- import { ServerMessageDef, Serializer, ServerFrame, EmitData, ConnDescriptor, Adapter, PresenceStore, Contract, RoleOf, NodeStat, SharedEvents, SharedServerRequests, ClientInput, Output, SharedRequests, ServerInput, AnyData, RoleRequests, Events, DataOf, RoleTopics, SharedTopics, ServerEvents, ServerEmit, ServerData } from '@super-line/core';
3
- import { WebSocket } from 'ws';
1
+ import { ServerMessageDef, RawConn, Serializer, ServerFrame, EmitData, ConnDescriptor, Adapter, PresenceStore, Contract, RoleOf, NodeStat, SharedEvents, SharedServerRequests, ClientInput, Output, SharedRequests, ServerInput, AnyData, RoleRequests, Events, DataOf, RoleTopics, SharedTopics, EventData, ServerTransport, Handshake } from '@super-line/core';
4
2
 
5
- /** Backpressure policy: what to do when a connection's send buffer grows too large. */
6
- interface Backpressure {
7
- /** Buffer size (bytes) above which {@link Backpressure.onExceed} kicks in. */
8
- maxBufferedBytes: number;
9
- /** `'close'` (default) drops the connection with code 1013; `'drop'` skips the frame. */
10
- onExceed?: 'close' | 'drop';
11
- }
12
3
  /**
13
4
  * A single client connection, passed to handlers as the third argument.
14
5
  *
15
- * Node-local: `conn` objects live on the node that accepted the upgrade, so don't
6
+ * Node-local: `conn` objects live on the node that accepted the connection, so don't
16
7
  * stash one to reach a user later — cross-node delivery goes through the Adapter
17
8
  * (use a per-user room instead). Generic over the events it may emit (scoped by
18
9
  * role), its `ctx`, and its `role`.
19
10
  */
20
11
  declare class Conn<Ev = Record<string, ServerMessageDef>, Ctx = unknown, Role extends string = string, Data = unknown> {
21
- /** The underlying `ws` socket. `conn.ws.terminate()` simulates a drop in tests. */
22
- readonly ws: WebSocket;
12
+ /** The underlying transport connection. `conn.terminate()` simulates a drop in tests. */
13
+ readonly raw: RawConn;
23
14
  /** Server-assigned unique id for this connection (stable for its lifetime). */
24
15
  readonly id: string;
25
16
  /** This connection's role (the literal resolved by `authenticate`). */
@@ -27,12 +18,15 @@ declare class Conn<Ev = Record<string, ServerMessageDef>, Ctx = unknown, Role ex
27
18
  /** The context `authenticate` returned for this connection. */
28
19
  readonly ctx: Ctx;
29
20
  private readonly serializer;
30
- private readonly backpressure?;
21
+ /** Optional inspector tap: called with each `emit` so the server can mirror it to inspectors. */
22
+ private readonly onEmit?;
31
23
  /** Namespaced channels (rooms + topics) this connection belongs to. */
32
24
  readonly channels: Set<string>;
33
25
  /** Mutable per-connection scratch state, typed per role by the contract's `data` schema. */
34
26
  data: Data;
35
- /** When this connection was accepted (`Date.now()` at the upgrade). */
27
+ /** The client↔server transport (wire) this connection was accepted on (set by the server at accept). */
28
+ transport?: string;
29
+ /** When this connection was accepted (`Date.now()`). */
36
30
  readonly connectedAt: number;
37
31
  /** When the server last sent a heartbeat ping to this connection (managed by the server). */
38
32
  lastPingAt?: number;
@@ -41,23 +35,26 @@ declare class Conn<Ev = Record<string, ServerMessageDef>, Ctx = unknown, Role ex
41
35
  /** Pings sent since the last pong; drives reaping (managed by the server). */
42
36
  missedPongs: number;
43
37
  constructor(
44
- /** The underlying `ws` socket. `conn.ws.terminate()` simulates a drop in tests. */
45
- ws: WebSocket,
38
+ /** The underlying transport connection. `conn.terminate()` simulates a drop in tests. */
39
+ raw: RawConn,
46
40
  /** Server-assigned unique id for this connection (stable for its lifetime). */
47
41
  id: string,
48
42
  /** This connection's role (the literal resolved by `authenticate`). */
49
43
  role: Role,
50
44
  /** The context `authenticate` returned for this connection. */
51
- ctx: Ctx, serializer: Serializer, backpressure?: Backpressure | undefined);
52
- private overBackpressure;
45
+ ctx: Ctx, serializer: Serializer,
46
+ /** Optional inspector tap: called with each `emit` so the server can mirror it to inspectors. */
47
+ onEmit?: ((event: string, data: unknown) => void) | undefined);
53
48
  /** Encode and send a frame (unicast, e.g. req/res). */
54
49
  send(frame: ServerFrame): void;
55
50
  /** Forward an already-encoded frame (fan-out path; encoded once at the source). */
56
51
  sendRaw(payload: string | Uint8Array): void;
57
52
  /** Push an event to THIS connection (node-local). Scoped to the role's events. */
58
53
  emit<E extends keyof Ev>(event: E, data: EmitData<Ev[E]>): void;
59
- /** Close the underlying socket. */
54
+ /** Graceful close of the underlying transport connection. */
60
55
  close(): void;
56
+ /** Hard close with no handshake — used by heartbeat reaping. */
57
+ terminate(): void;
61
58
  }
62
59
 
63
60
  /**
@@ -127,12 +124,17 @@ type Handlers<C extends Contract, A> = ([keyof SharedRequests<C>] extends [never
127
124
  };
128
125
  /** Context passed to middleware and lifecycle hooks about the current operation. */
129
126
  interface MiddlewareInfo {
130
- /** Whether this is a request or a topic subscribe. */
131
- kind: 'request' | 'subscribe';
132
- /** The request/topic name. */
127
+ /** Whether this is a request, a topic subscribe, or a bus event delivery. */
128
+ kind: 'request' | 'subscribe' | 'event';
129
+ /** The request/topic/event name. */
133
130
  name: string;
134
- /** The connection the operation is on (`conn.role` available). */
135
- conn: Conn;
131
+ /** The connection the operation is on, if any (`conn.role` available). Absent for bus events. */
132
+ conn?: Conn;
133
+ }
134
+ /** Metadata passed to a {@link SuperLineServer.subscribe} callback alongside the event payload. */
135
+ interface BusMeta {
136
+ /** The node that published the event. Equals `srv.nodeId` for a same-node publish (local echo). */
137
+ from: string;
136
138
  }
137
139
  /**
138
140
  * Flat middleware run before request/subscribe handlers. Call `next()` to proceed,
@@ -186,7 +188,7 @@ interface ConnTarget<C extends Contract> {
186
188
  emit<E extends keyof SharedEvents<C>>(event: E, data: EmitData<SharedEvents<C>[E]>): void;
187
189
  /**
188
190
  * Send a shared server→client request and await the client's typed reply
189
- * (cross-node). Rejects with a `TIMEOUT` `SocketError` if no live node owns
191
+ * (cross-node). Rejects with a `TIMEOUT` `SuperLineError` if no live node owns
190
192
  * the connection or the client doesn't answer in time.
191
193
  */
192
194
  request<M extends keyof SharedServerRequests<C>>(name: M, input: ClientInput<SharedServerRequests<C>[M]>, opts?: {
@@ -208,18 +210,21 @@ interface RoleLens<C extends Contract, R extends RoleOf<C>> {
208
210
  /** Publish to a topic in role `R`'s surface (reaches that role's subscribers). */
209
211
  publish<T extends keyof RoleTopics<C, R>>(topic: T, data: EmitData<RoleTopics<C, R>[T]>): void;
210
212
  }
211
- /** Options for {@link createSocketServer}. */
212
- interface ServerOptions<C extends Contract, A extends AuthResult<C>> {
213
- /** The `http.Server` to attach to (compose with Express/Fastify/Hono). */
214
- server?: Server;
213
+ /** Options for {@link createSuperLineServer}. */
214
+ interface SuperLineServerOptions<C extends Contract, A extends AuthResult<C>> {
215
+ /** Client↔server transports to accept connections on (e.g. `webSocketServerTransport({ server })`). */
216
+ transports: ServerTransport[];
215
217
  /** Wire serializer; MUST match the client. Defaults to `jsonSerializer`. */
216
218
  serializer?: Serializer;
217
219
  /** Cross-node fan-out adapter. Defaults to a per-server in-memory adapter. */
218
220
  adapter?: Adapter;
219
- /** Only handle upgrades for this pathname; others are left untouched. */
220
- path?: string;
221
- /** Runs at the HTTP upgrade. Return { role, ctx }, or throw to reject with 401. */
222
- authenticate: (req: IncomingMessage) => Awaitable<A>;
221
+ /**
222
+ * Friendly name for this node, surfaced in `srv.nodeName`, the cluster descriptor, and the
223
+ * Control Center topology. Defaults to `SUPER_LINE_NODE_NAME` or a short slice of `nodeId`.
224
+ */
225
+ nodeName?: string;
226
+ /** Authenticate a connection from its normalized {@link Handshake}. Return { role, ctx }, or throw to reject. */
227
+ authenticate: (handshake: Handshake) => Awaitable<A>;
223
228
  /** Stable user key for a connection (powers `cluster.byUser`, `isOnline`, and `toUser`). */
224
229
  identify?: (conn: Conn) => string | undefined;
225
230
  /** Extra fields merged into the connection's cluster descriptor (e.g. `{ plan }`). `ctx` is never auto-serialized. */
@@ -238,8 +243,14 @@ interface ServerOptions<C extends Contract, A extends AuthResult<C>> {
238
243
  interval?: number;
239
244
  maxMissed?: number;
240
245
  } | false;
241
- /** Guard against slow consumers: when a connection's send buffer exceeds the limit, close or drop. */
242
- backpressure?: Backpressure;
246
+ /**
247
+ * Enable the read-only Control Center inspector: emit `msg.*` telemetry and accept inspector
248
+ * clients. The WS transport must also be created with `inspector: true` to negotiate the
249
+ * `superline.inspector.v1` subprotocol. **Default off; dev / trusted-network only.**
250
+ */
251
+ inspector?: boolean | {
252
+ redact?: string[];
253
+ };
243
254
  /** Called once per accepted connection. */
244
255
  onConnection?: (conn: Conn, ctx: CtxUnion<A>) => void;
245
256
  /** Called when a connection closes, with the WebSocket close `code`. */
@@ -247,10 +258,12 @@ interface ServerOptions<C extends Contract, A extends AuthResult<C>> {
247
258
  /** Called for any error thrown in middleware/handlers (after the client is replied to). */
248
259
  onError?: (error: unknown, info: MiddlewareInfo) => void;
249
260
  }
250
- /** A running super-line server, returned by {@link createSocketServer}. */
251
- interface SocketServer<C extends Contract, A extends AuthResult<C>> {
261
+ /** A running super-line server, returned by {@link createSuperLineServer}. */
262
+ interface SuperLineServer<C extends Contract, A extends AuthResult<C>> {
252
263
  /** This node's stable id (unique per server process). */
253
264
  readonly nodeId: string;
265
+ /** This node's friendly name (from `nodeName`/`SUPER_LINE_NODE_NAME`, else a short `nodeId` slice). */
266
+ readonly nodeName: string;
254
267
  /** Synchronous, node-local introspection (connections, rooms, topics on this process). */
255
268
  readonly local: LocalView;
256
269
  /** Asynchronous, cluster-wide introspection backed by the adapter's presence directory. */
@@ -262,34 +275,37 @@ interface SocketServer<C extends Contract, A extends AuthResult<C>> {
262
275
  /** Target all of a user's connections (by `identify` key) across nodes (emit/disconnect). */
263
276
  toUser(userId: string): UserTarget<C>;
264
277
  /** Register handlers for shared + per-role requests (chainable). */
265
- implement(handlers: Handlers<C, A>): SocketServer<C, A>;
278
+ implement(handlers: Handlers<C, A>): SuperLineServer<C, A>;
266
279
  /** Mixed-role connection group; broadcast() sends a shared contract event to members. */
267
280
  room(name: string): Room<C>;
268
281
  /** Publish a SHARED topic to all subscribers (server-only publish). */
269
282
  publish<T extends keyof SharedTopics<C>>(topic: T, data: EmitData<SharedTopics<C>[T]>): void;
283
+ /**
284
+ * Subscribe SERVER-side to a shared topic, cluster-wide. The callback fires for a
285
+ * publish from any node — including this one (local echo, delivered in-process with
286
+ * no round-trip). `meta.from` is the publishing node; self-exclude with
287
+ * `if (meta.from === srv.nodeId) return`. Returns an unsubscribe fn.
288
+ */
289
+ subscribe<T extends keyof SharedTopics<C>>(topic: T, handler: (data: EventData<SharedTopics<C>[T]>, meta: BusMeta) => void): () => void;
270
290
  /** Lens for role-scoped sends, e.g. forRole('user').publish('feed', data). */
271
291
  forRole<R extends RoleOf<C>>(role: R): RoleLens<C, R>;
272
- /** Broadcast a typed event to all OTHER server nodes (at-most-once, excludes self). */
273
- emitServer<E extends keyof ServerEvents<C>>(event: E, data: ServerEmit<ServerEvents<C>[E]>): void;
274
- /** Listen for inter-server events from other nodes. Returns an unsubscribe fn. */
275
- onServer<E extends keyof ServerEvents<C>>(event: E, handler: (data: ServerData<ServerEvents<C>[E]>) => void): () => void;
276
292
  close(): Promise<void>;
277
293
  }
278
294
  /**
279
295
  * Create a server bound to a contract. Attach it to an `http.Server`, then call
280
- * {@link SocketServer.implement} with your handlers. `authenticate` resolves each
296
+ * {@link SuperLineServer.implement} with your handlers. `authenticate` resolves each
281
297
  * connection's `{ role, ctx }` at the upgrade.
282
298
  *
283
299
  * @param contract - the shared contract.
284
300
  * @param opts - server options; `authenticate` is required.
285
- * @returns the {@link SocketServer}.
286
- * @throws nothing directly; handler throws become a typed `SocketError` to the client.
301
+ * @returns the {@link SuperLineServer}.
302
+ * @throws nothing directly; handler throws become a typed `SuperLineError` to the client.
287
303
  *
288
304
  * @example
289
305
  * ```ts
290
- * const srv = createSocketServer(api, {
291
- * server,
292
- * authenticate: (req) => ({ role: 'user' as const, ctx: { id: '1' } }),
306
+ * const srv = createSuperLineServer(api, {
307
+ * transports: [webSocketServerTransport({ server })],
308
+ * authenticate: (h) => ({ role: 'user' as const, ctx: { id: '1' } }),
293
309
  * })
294
310
  * srv.implement({
295
311
  * shared: { join: async ({ room }, _ctx, conn) => { srv.room(room).add(conn); return { ok: true } } },
@@ -297,6 +313,6 @@ interface SocketServer<C extends Contract, A extends AuthResult<C>> {
297
313
  * })
298
314
  * ```
299
315
  */
300
- declare function createSocketServer<C extends Contract, A extends AuthResult<C>>(contract: C, opts: ServerOptions<C, A>): SocketServer<C, A>;
316
+ declare function createSuperLineServer<C extends Contract, A extends AuthResult<C>>(contract: C, opts: SuperLineServerOptions<C, A>): SuperLineServer<C, A>;
301
317
 
302
- export { type AuthResult, type Backpressure, type ClusterView, Conn, type ConnTarget, type Handlers, type LocalView, MemoryBus, type Middleware, type MiddlewareInfo, type RoleLens, type Room, type ServerOptions, type SocketServer, type UserTarget, createInMemoryAdapter, createSocketServer };
318
+ export { type AuthResult, type BusMeta, type ClusterView, Conn, type ConnTarget, type Handlers, type LocalView, MemoryBus, type Middleware, type MiddlewareInfo, type RoleLens, type Room, type SuperLineServer, type SuperLineServerOptions, type UserTarget, createInMemoryAdapter, createSuperLineServer };