--- url: 'https://super-line.dogar.biz/guide/introduction.md' --- # Why super-line super-line is a typesafe realtime data bus for TypeScript. You write **one contract**; the server implements it and the client calls it with full end-to-end type inference — and **zero codegen** — over the wire of your choice: **WebSocket, HTTP (SSE / long-poll), or libp2p/WebRTC**, swapped in one line. ## The idea Realtime apps usually glue together hand-maintained event-name constants, untyped payloads, and ad-hoc validation. super-line replaces all of that with a single `defineContract({...})` object that **both** sides import. From that one declaration you get: * **Types on both ends** — the server's handlers and the client's calls are inferred from the same source, so they can't drift. * **Runtime validation** — the same schemas that type your payloads also validate them. The server rejects malformed input automatically. * **Five interaction flavors** over one connection — requests, events, topics, rooms, and node-to-node messaging. * **Any wire** — the same contract and the same code run over WebSocket, HTTP, or libp2p/WebRTC. The transport is one line; everything above it is identical. See [Transports](./transports). ## Two axes: direction and role The contract is organized along two axes: * **Direction** — `clientToServer` (requests) and `serverToClient` (events & topics). Each is a named key on the contract, so there are no positional generics to get backwards. * **Role** — a `shared` base plus one block per client role (`user`, `agent`, …). A connection's role is fixed at connect (by `authenticate`) and decides which surface — and which `ctx` — it gets. A cross-role call is rejected with `NOT_FOUND`. See [The contract](./the-contract) for the full model. ## How it compares | | super-line | Socket.IO | tRPC | | --- | :---: | :---: | :---: | | Typesafe contract | ✅ | ⚠️ types-only | ✅ | | Runtime validation | ✅ | ❌ | ✅ | | Per-role contracts | ✅ | ❌ | ❌ | | Rooms & topics | ✅ | ⚠️ rooms only | subscriptions | | Inter-server messaging | ✅ | ✅ | ❌ | | Pluggable wire (WS · HTTP · WebRTC) | ✅ | ⚠️ WS + polling | ⚠️ link-dependent | Socket.IO splits its types into `ClientToServerEvents` / `ServerToClientEvents` / `InterServerEvents` interfaces you wire as **positional generics** (easy to swap) with no runtime validation. super-line keeps the directional split but in **one shared object**, validates inbound automatically, and adds **per-role contracts**. See the full [comparison & FAQ](./comparison-faq). Next: [Getting started](./getting-started). --- --- url: 'https://super-line.dogar.biz/guide/getting-started.md' --- # Getting started The wire is **pluggable** — WebSocket by default, with HTTP (SSE / long-poll) and libp2p also available (see [Transports](./transports)). This guide uses WebSocket; everything above the transport line is identical on every wire. ## 1. Scaffold the project Create a folder and three source files. The contract is the one module **both** sides import — that's what keeps them in sync. ```bash mkdir my-line && cd my-line npm init -y mkdir src ``` You're building toward this layout: ``` my-line/ ├─ package.json ├─ tsconfig.json └─ src/ ├─ contract.ts # the single source of truth — imported by both sides ├─ server.ts # implements the contract └─ client.ts # calls it, fully typed ``` ## 2. Install You need `core` (the contract), `server`, `client`, a transport, and `zod` for the schemas. ::: code-group ```bash [pnpm] pnpm add @super-line/core @super-line/server @super-line/client @super-line/transport-websocket zod pnpm add -D tsx typescript ``` ```bash [npm] npm install @super-line/core @super-line/server @super-line/client @super-line/transport-websocket zod npm install -D tsx typescript ``` ```bash [yarn] yarn add @super-line/core @super-line/server @super-line/client @super-line/transport-websocket zod yarn add -D tsx typescript ``` ::: We use [`tsx`](https://tsx.is) to run TypeScript directly — no build step while you're learning. Now wire up the two config files. super-line is ESM-only, so `package.json` needs `"type": "module"`: ::: code-group ```json [package.json] { "name": "my-line", "type": "module", "scripts": { "server": "tsx src/server.ts", "client": "tsx src/client.ts" } } ``` ```json [tsconfig.json] { "compilerOptions": { "target": "ES2022", "module": "ESNext", "moduleResolution": "Bundler", "strict": true, "skipLibCheck": true, "types": ["node"] }, "include": ["src"] } ``` ::: ## 3. Define the contract The contract is split by **direction** (`clientToServer` / `serverToClient`) and scoped by **role** (a `shared` base plus one block per role). This one file holds every interaction in the app — a request, a pushed event, and a subscribable topic. ```ts [src/contract.ts] import { z } from 'zod' import { defineContract } from '@super-line/core' export const chat = defineContract({ shared: { clientToServer: { // request: input is validated, output is typed back to the caller join: { input: z.object({ room: z.string() }), output: z.object({ ok: z.boolean() }) }, }, serverToClient: { // event: the server pushes this; clients listen with `.on()` message: { payload: z.object({ room: z.string(), text: z.string(), from: z.string() }) }, // topic: same shape, but `subscribe: true` lets clients `.subscribe()` to it presence: { payload: z.object({ room: z.string(), count: z.number() }), subscribe: true }, }, }, roles: { user: { clientToServer: { send: { input: z.object({ room: z.string(), text: z.string() }), output: z.object({ id: z.string() }) }, }, }, }, }) ``` See [The contract](./the-contract) for the full model — directions, roles, and every interaction flavor. ## 4. Implement the server The server owns rooms and authorization. `authenticate` runs once per connection and fixes the role; every handler then receives the validated input plus the `ctx` you returned. ```ts [src/server.ts] import http from 'node:http' import { randomUUID } from 'node:crypto' import { createSuperLineServer } from '@super-line/server' import { webSocketServerTransport } from '@super-line/transport-websocket' import { chat } from './contract' const server = http.createServer() // or hand in your Express / Fastify http.Server const srv = createSuperLineServer(chat, { transports: [webSocketServerTransport({ server })], authenticate: (h) => { const name = h.query.name // the Handshake: { transport, headers, query, peer?, raw } if (!name) throw new Error('unauthorized') // throw → rejected at the WS upgrade, no socket return { role: 'user' as const, ctx: { name } } // ctx is handed to every handler }, }) srv.implement({ shared: { join: async ({ room }, _ctx, conn) => { srv.room(room).add(conn) // membership is server-controlled srv.forRole('user').publish('presence', { room, count: srv.room(room).size }) // push the topic return { ok: true } }, }, user: { send: async ({ room, text }, ctx) => { srv.room(room).broadcast('message', { room, text, from: ctx.name }) // → every client.on('message') return { id: randomUUID() } }, }, }) server.listen(3000, () => console.log('super-line server on ws://localhost:3000')) ``` ## 5. Write the client The client imports the **same** contract, so `join`, `send`, `on`, and `subscribe` are all inferred — wrong event names and bad payloads are compile errors, not runtime surprises. ```ts [src/client.ts] import { createSuperLineClient } from '@super-line/client' import { webSocketClientTransport } from '@super-line/transport-websocket' import { chat } from './contract' const client = createSuperLineClient(chat, { transport: webSocketClientTransport({ url: 'ws://localhost:3000' }), role: 'user', // narrows the surface to shared ∪ user; verified by authenticate params: { name: 'ada' }, // carried in the handshake → readable as h.query.name }) client.on('message', (m) => console.log(`💬 ${m.from}: ${m.text}`)) // event client.subscribe('presence', (p) => console.log(`👥 ${p.count} online in ${p.room}`)) // topic await client.join({ room: 'lobby' }) await client.send({ room: 'lobby', text: 'hello, super-line' }) // request → typed { id } await new Promise((r) => setTimeout(r, 300)) // let the pushes land, then exit client.close() ``` ::: warning Node 18 / 20: provide a WebSocket The client uses the global `WebSocket`, which exists in browsers and **Node 22+**. On older Node, install `ws` and pass it through: `webSocketClientTransport({ url, WebSocket })`. ::: ## 6. Run it Start the server, then the client in a second terminal: ::: code-group ```bash [Terminal 1 · server] npm run server ``` ```bash [Terminal 2 · client] npm run client ``` ::: The client prints: ```ansi 👥 1 online in lobby 💬 ada: hello, super-line ``` ## What just happened Each call you wrote maps to one of super-line's wire patterns, all sharing one connection and one contract: | Your client call | Pattern | What it does | | --- | --- | --- | | `await client.send(…)` | **Request** | Validated input in, typed `{ id }` back — like an RPC. | | `client.on('message', …)` | **Event** | The server pushes; you listen. Fire-and-forget. | | `client.subscribe('presence', …)` | **Topic** | You opt in; the server fans out to every subscriber. | Rename a field in `contract.ts` and the other side stops compiling — that's the whole point. And types aren't trust: every inbound payload is re-validated against the schema on the server, so even an untyped peer can't slip a bad message through. ## Next steps * [The contract](./the-contract) — roles, direction, and the interaction flavors in depth. * [Events & rooms](./events-rooms) and [Topics](./topics) — the push patterns you just used. * [Roles & auth](./roles-auth) — give `agent` (or admin) connections a different surface. * [React](./react) — the same contract through typed hooks. * [Scaling & adapters](./scaling-adapters) — go multi-node with one extra line. * [API reference](/reference/) — every export, option, and type. --- --- url: 'https://super-line.dogar.biz/guide/the-contract.md' --- # The contract The contract is the single source of truth, imported by both server and client. It has two axes — **role** (outer) and **direction** (inner) — and each entry's shape picks an interaction flavor. ```ts import { z } from 'zod' import { defineContract } from '@super-line/core' export const api = defineContract({ shared: { // every role inherits these clientToServer: { /* requests */ }, serverToClient: { /* events + topics */ }, }, roles: { // each role sees shared ∪ its own block user: { clientToServer: {…}, serverToClient: {…} }, agent: { clientToServer: {…}, serverToClient: {…} }, }, }) ``` ## Direction Within `shared` and each role block there are two directions: * **`clientToServer`** — requests the client may call. * **`serverToClient`** — events and topics the client may receive. Direction is encoded as **named keys**, never positional generics — you can't accidentally swap them, and there's nothing to keep in sync between the two sides. ## The five flavors | Flavor | Contract entry | Who initiates | | --- | --- | --- | | **request** | `clientToServer: { input, output }` | client calls, awaits one reply | | **event** | `serverToClient: { payload }` | server pushes to recipients it picks | | **topic** | `serverToClient: { payload, subscribe: true }` | client subscribes; server publishes | | **room** | server API (`srv.room(...)`) | server controls membership; broadcasts a shared event | A `serverToClient` entry is an **event** by default; adding `subscribe: true` turns it into a **topic** the client opts into. (Topics fold into `serverToClient` so there's just one axis to learn.) A **shared topic** is also the **cluster event bus**: the same declaration types `server.publish` (any node fans out), `server.subscribe` (in-process, cluster-wide server-side consumers with local echo), and `client.subscribe` over WS — one decl, three subscriber kinds. See [The cluster event bus](./cluster-event-bus). ## Roles Each role is an audience with its own surface. The **effective surface** for a role is `shared ∪ roles[R]` — for both requests and events/topics. A `user` and an `agent` can have entirely different verbs: ```ts roles: { user: { clientToServer: { say: { input: z.object({ text: z.string() }), output: z.object({ id: z.string() }) } } }, agent: { clientToServer: { announce: { input: z.object({ text: z.string() }), output: z.object({ id: z.string() }) } } }, } ``` * **Type-level**: a client created with `role: 'agent'` only sees the agent surface; `agent.say(...)` is a compile error. * **Runtime**: the server resolves the role in `authenticate` and rejects any call outside `shared ∪ roles[role]` with `NOT_FOUND`. The role is a real security boundary, not just a typing convenience. See [Roles & auth](./roles-auth) for how the role is resolved and verified. ## Schemas Any [Standard Schema](https://standardschema.dev) validator works — Zod, Valibot, ArkType. The examples use Zod. The same schema both **types** the payload and **validates** it at runtime. Next: [Requests](./requests). --- --- url: 'https://super-line.dogar.biz/guide/transports.md' --- # Choose your wire super-line separates **what** travels — your typed contract: requests, events, topics, validated and routed by a server-authoritative core — from **how** it travels: the **transport**. That separation is the point. The same server, the same client, the same handlers run over a WebSocket, an HTTP/SSE stream, or a libp2p/WebRTC peer connection. **The transport is one line; everything above it is identical.** ```ts // the ONLY thing that changes between wires: webSocketClientTransport({ url: 'ws://localhost:3000' }) // WebSocket httpClientTransport({ url: 'http://localhost:3000' }) // HTTP — SSE / long-poll libp2pClientTransport({ node, multiaddr }) // libp2p / WebRTC loopbackTransport.client() // in-memory (tests) ``` ```ts const client = createSuperLineClient(contract, { transport: webSocketClientTransport({ url: 'ws://localhost:3000' }), // ← swap this one line role: 'user', }) await client.send({ room: 'lobby', text: 'hi' }) // identical on every wire ``` A server can even accept **several at once** on one `http.Server`: ```ts createSuperLineServer(contract, { transports: [webSocketServerTransport({ server }), httpServerTransport({ server })], authenticate, }) ``` WebSocket uses the HTTP `upgrade` channel and HTTP uses the `request` channel, so they coexist without collision — a browser that can't open a WebSocket falls back to HTTP against the very same server. ## Which wire? | If you need… | Use | Package | |---|---|---| | The default — lowest latency, full-duplex, broadest support | **WebSocket** | [`@super-line/transport-websocket`](./transport-websocket) | | To survive restrictive networks / proxies that block or buffer WebSocket | **HTTP** (SSE or long-poll) | [`@super-line/transport-http`](./transport-http) | | Peer-to-peer / **WebRTC** / WebTransport, browser↔server with no signaling code | **libp2p** | [`@super-line/transport-libp2p`](./transport-libp2p) | | Fast, deterministic tests with a real server + client in one process | **Loopback** | [`@super-line/transport-loopback`](./transport-loopback) | Start with WebSocket. Reach for HTTP as a fallback wire, libp2p when you want WebRTC/p2p, and loopback in your test suite. ## One handshake, every wire `authenticate` always receives a normalized **Handshake** — the same shape regardless of transport — so your auth code is written once: ```ts authenticate: (h) => { // h: { transport, headers, query, peer?, raw } const token = h.query.token // WS/HTTP carry it on the URL; libp2p carries it in the first frame // h.peer = { id, addr } for peer transports (the verified PeerId) return { role: 'user', ctx: verify(token) } } ``` ## What every transport shares The transport only moves opaque bytes over a *logical* connection and never inspects a frame. Everything else lives in the core and behaves identically on every wire: * **Server-authority** — roles fixed at connect; the server owns rooms/topics and validates every inbound message; cross-role calls → `NOT_FOUND`. * **Liveness** — app-level ping/pong frames the core sends and answers; no per-transport heartbeat. * **Reconnect** — a dropped logical connection re-authenticates and re-subscribes (no session resume). The transport hides the *physical* churn (HTTP's many requests, SSE reconnects, peer re-dials) beneath that one logical connection. * **Validation, rooms, topics, serialization, and the cluster `Adapter`** — unchanged. The transport is just the pipe. ::: tip Transports vs adapters A **transport** is the *client↔server* wire (this page). An [**adapter**](./scaling-adapters) is the *server↔server* fan-out substrate for multi-node clusters (Redis, libp2p, …). They're independent — you pick each separately. ::: Next: pick a wire — [WebSocket](./transport-websocket) · [HTTP](./transport-http) · [libp2p & WebRTC](./transport-libp2p) · [Loopback](./transport-loopback). --- --- url: 'https://super-line.dogar.biz/guide/transport-websocket.md' --- # WebSocket transport The default wire: a full-duplex WebSocket. Lowest latency, broadest support, and the closest match to a classic realtime connection. Provided by `@super-line/transport-websocket`. ```bash pnpm add @super-line/transport-websocket ``` ## Server `webSocketServerTransport` attaches to an `http.Server` — compose it with Express/Fastify/Hono, or a bare `http.createServer()`. ```ts import http from 'node:http' import { createSuperLineServer } from '@super-line/server' import { webSocketServerTransport } from '@super-line/transport-websocket' const server = http.createServer() const srv = createSuperLineServer(contract, { transports: [webSocketServerTransport({ server })], authenticate: (h) => ({ role: h.query.role, ctx: verify(h.query.token) }), }) server.listen(3000) ``` **Options:** | Option | Default | Notes | |---|---|---| | `server` | — | the `http.Server` to attach to (required) | | `path` | any | only handle upgrades for this pathname; others pass through | | `backpressure` | off | `{ maxBufferedBytes, onExceed: 'close' \| 'drop' }` — guard against slow consumers (the byte-buffer threshold is WS-specific) | | `inspector` | off | accept Control Center inspector clients on the `superline.inspector.v1` subprotocol — see [Control Center](./control-center) | `path`, `backpressure`, and `inspector` are WebSocket-transport concerns, so they live on the transport, not on the server. ## Client ```ts import { createSuperLineClient } from '@super-line/client' import { webSocketClientTransport } from '@super-line/transport-websocket' const client = createSuperLineClient(contract, { transport: webSocketClientTransport({ url: 'ws://localhost:3000' }), role: 'user', params: { token }, // carried on the URL query → readable as h.query.token in authenticate }) ``` In a **browser** (and Node 22+), `WebSocket` is a global — nothing to configure. On **older Node**, pass an implementation: ```ts import WebSocket from 'ws' webSocketClientTransport({ url, WebSocket }) ``` ## Behavior notes * A rejected `authenticate` becomes a **`401` at the HTTP upgrade with no socket opened** — efficient, and a real status. (To the client this looks like a connection drop; see [Reconnection & delivery](./reconnection-delivery).) * Heartbeat is **app-level ping/pong frames** the core manages — not the WebSocket protocol ping — so it's identical across every transport. * `params` (and `role`) ride the URL query string and surface in `authenticate` as `h.query`. Next: [HTTP — SSE & long-poll](./transport-http) · back to [Choose your wire](./transports). --- --- url: 'https://super-line.dogar.biz/guide/transport-http.md' --- # HTTP transport — SSE & long-poll For environments where WebSocket is blocked or buffered (corporate proxies, some load balancers, locked-down networks), super-line runs over plain HTTP: a **Server-Sent Events** stream or **long-poll** downstream, plus a `POST` upstream — all over one logical connection. Provided by `@super-line/transport-http`. ```bash pnpm add @super-line/transport-http ``` It carries the exact same contract as WebSocket. The transport hides the HTTP mechanics — the session that spans many requests, EventSource reconnects — so the core still sees one logical connection. ## Server Mount it on an `http.Server` — including **the same one your WebSocket transport uses** (WS owns the `upgrade` channel, HTTP owns `request`, so they coexist): ```ts import { httpServerTransport } from '@super-line/transport-http' import { webSocketServerTransport } from '@super-line/transport-websocket' createSuperLineServer(contract, { transports: [ webSocketServerTransport({ server }), // preferred wire httpServerTransport({ server }), // automatic fallback on the same server ], authenticate, }) ``` **Options:** `basePath` (default `/superline`), `mode` (`'sse' | 'longpoll' | 'both'`, default `'both'`), `sessionTimeout`, `keepalive` (SSE comment interval), `pollTimeout`, `maxBodyBytes`, `cors`. ## Client In a **browser**, `EventSource` and `fetch` are globals — nothing to configure: ```ts import { httpClientTransport } from '@super-line/transport-http' createSuperLineClient(contract, { transport: httpClientTransport({ url: 'https://api.example.com', mode: 'sse' }), role: 'user', }) ``` In **Node**, `EventSource` is **not** a global (even in current versions). For SSE mode, inject one — the [`eventsource`](https://www.npmjs.com/package/eventsource) package: ```ts import { EventSource } from 'eventsource' httpClientTransport({ url, EventSource }) // SSE httpClientTransport({ url, mode: 'longpoll' }) // long-poll needs only `fetch` ``` ## SSE vs long-poll | | SSE | long-poll | |---|---|---| | Downstream | a held `text/event-stream` (EventSource) | a sequence of held `GET`s that return and re-issue | | Needs `EventSource` | yes (browser global; Node: inject) | no — `fetch` only | | Behind SSE-buffering proxies | can stall | works (plain request/response) | Prefer **SSE**; switch to **`mode: 'longpoll'`** behind proxies that buffer event streams. ## Notes * **Heavier than WebSocket for large binary payloads** — frames are base64-encoded to travel safely over an SSE `data:` line or a JSON body (≈ +33%). It's the compatibility/fallback wire, not the performance wire. * The transport sets `Cache-Control: no-cache, no-transform` + `X-Accel-Buffering: no` and writes periodic keepalive comments to survive idle-proxy reaping. * Same `authenticate(handshake)`, same app-level ping/pong liveness, same reconnect model as every wire. Next: [libp2p & WebRTC](./transport-libp2p) · back to [Choose your wire](./transports). --- --- url: 'https://super-line.dogar.biz/guide/transport-libp2p.md' --- # libp2p & WebRTC transport Carry the contract over a **libp2p protocol stream** — which means WebSocket, **WebRTC** (direct or relayed), or WebTransport, with libp2p handling all the connection establishment and signaling for you. Provided by `@super-line/transport-libp2p`. ```bash pnpm add @super-line/transport-libp2p libp2p ``` The big win: **you write no WebRTC signaling.** You hand the transport a libp2p node configured with the connectivity you want, and super-line rides its streams. ## Bring your own node The transport takes a started `Libp2p` node — *you* choose its transports (`@libp2p/websockets`, `@libp2p/webrtc`, `@libp2p/webtransport`), encryption, muxer, and listen addresses. The package's only runtime deps are `@super-line/core`, `@libp2p/interface`, and `@libp2p/utils`; **`libp2p` is a peer dependency** (you already build the node). > This is a **separate** node from [`@super-line/adapter-libp2p`](./adapter-libp2p), which uses gossipsub for *server↔server* fan-out. Transports carry client↔server traffic; adapters carry node↔node fan-out. ```ts import { createLibp2p } from 'libp2p' import { webSockets } from '@libp2p/websockets' import { noise } from '@chainsafe/libp2p-noise' import { yamux } from '@chainsafe/libp2p-yamux' import { libp2pServerTransport, libp2pClientTransport } from '@super-line/transport-libp2p' // server: a node that listens, with the protocol registered on it const node = await createLibp2p({ addresses: { listen: ['/ip4/0.0.0.0/tcp/9001/ws'] }, transports: [webSockets()], connectionEncrypters: [noise()], streamMuxers: [yamux()], }) createSuperLineServer(contract, { transports: [libp2pServerTransport({ node })], authenticate }) // client: a node that dials the server's multiaddr(s) createSuperLineClient(contract, { transport: libp2pClientTransport({ node: clientNode, multiaddr: node.getMultiaddrs() }), role: 'user', }) ``` For **browser WebRTC**, the client node uses `@libp2p/webrtc` (`webRTCDirect()` for a publicly UDP-reachable server, or `webRTC()` relayed through a `circuit-relay-v2` node behind NAT); the server node advertises the matching multiaddr. libp2p performs the SDP/ICE handshake — super-line never touches it. ## Auth is the first frame libp2p has no HTTP headers or query string, so credentials ride the **first stream frame**: the client sends `{ role, params }`, and `authenticate` receives a Handshake with `transport: 'libp2p'`, `query: { role, ...params }`, and `peer: { id, addr }` — where `peer.id` is the **noise-verified PeerId**: ```ts authenticate: (h) => { if (h.transport === 'libp2p') { // h.peer.id is cryptographically verified — allow-list it, or read h.query.token } return { role: h.query.role, ctx: {} } } ``` ## Notes * Frames are **length-prefixed** on the stream (a raw libp2p message doesn't preserve frame boundaries under yamux) — invisible to your app. * Star topology only: the server is a distinguished, authoritative peer; clients dial it. There is no client-to-client data path through super-line. * Same `authenticate(handshake)`, same app-level ping/pong liveness, same reconnect model as every wire. * See `PLAN-transports.md` for the full WebRTC-direct vs circuit-relay connectivity matrix. Next: [Loopback (testing)](./transport-loopback) · back to [Choose your wire](./transports). --- --- url: 'https://super-line.dogar.biz/guide/transport-loopback.md' --- # Loopback transport (testing) Wire a **real** super-line server and client together in one process — no socket, no port, no `http.Server`. Provided by `@super-line/transport-loopback`. ```bash pnpm add -D @super-line/transport-loopback ``` It's the in-memory analogue of the `Adapter`: messages cross directly between the two endpoints. That makes it ideal for fast, deterministic tests that exercise the *whole* stack — `authenticate`, validation, rooms, topics, the server→client request bus — without the flakiness or teardown of real sockets. ```ts import { createLoopbackTransport } from '@super-line/transport-loopback' const loopback = createLoopbackTransport() const srv = createSuperLineServer(contract, { transports: [loopback.server], authenticate: (h) => ({ role: h.query.role, ctx: {} }), }) srv.implement({ user: { echo: async ({ text }) => ({ text }) } }) const client = createSuperLineClient(contract, { transport: loopback.client(), // each call() returns a fresh client connection to the same server role: 'user', params: { name: 'alice' }, // handshake params arrive as h.query in authenticate }) await client.echo({ text: 'hi' }) // a real round-trip through the real core, in-memory ``` ## Why use it * **Fast & deterministic** — no port binding, no socket timing, nothing to flake under parallel test load. * **Real core** — it's not a mock. The same server and client run; only the wire is in-memory. A test that passes on loopback exercises the actual request/response, event, topic, and heartbeat paths. * **Interface proof** — because the identical core runs over loopback and over WebSocket/HTTP/libp2p unchanged, it doubles as evidence the transport seam isn't WebSocket-shaped. Loopback supports the full lifecycle — `close()`, `terminate()`, and `server.stop()` — so reconnect and disconnect tests work too. See [Testing](./testing) for the broader test-harness patterns. Back to [Choose your wire](./transports). --- --- url: 'https://super-line.dogar.biz/guide/requests.md' --- # Requests A **request** is a client→server call that awaits one typed reply — super-line's request/response primitive. Declare it under `clientToServer` with an `input` and `output` schema: ```ts roles: { user: { clientToServer: { send: { input: z.object({ room: z.string(), text: z.string() }), output: z.object({ id: z.string() }) }, }, }, } ``` ## Server: handle it Handlers live in `implement`, keyed by role (and `shared`). The handler receives the **validated** input, the connection's `ctx`, and the `conn`: ```ts srv.implement({ user: { send: async ({ room, text }, ctx, conn) => { // input is already validated against the schema return { id: crypto.randomUUID() } // typed to the output schema }, }, }) ``` The server **always validates inbound input** before your handler runs — bad input rejects with a `VALIDATION` error and the handler never sees it. ## Client: call it The client is a typed proxy; call requests as methods: ```ts const out = await client.send({ room: 'lobby', text: 'hi' }) // ^? { id: string } ``` ### Timeouts and cancellation Each call accepts per-call options: ```ts await client.send(input, { timeoutMs: 5000 }) // override the default 30s await client.send(input, { signal: controller.signal }) // cancel via AbortController ``` A timed-out call rejects with `TIMEOUT`; an aborted call rejects with `BAD_REQUEST`. Set `timeoutMs: 0` to disable the timeout for a call. ## Errors Throw a typed [`SuperLineError`](./errors) from a handler and the client's promise rejects with the same `code`: ```ts import { SuperLineError } from '@super-line/core' send: async ({ room }, ctx) => { if (!ctx.canPost(room)) throw new SuperLineError('FORBIDDEN', 'not a member') // ... } ``` Unknown throws become `INTERNAL` (your internals aren't leaked to the client). ## Shared vs role requests Put a request in `shared.clientToServer` to make it callable by **every** role; put it in a role block to scope it. A request a connection's role can't see is rejected with `NOT_FOUND`. See [Roles & auth](./roles-auth). Next: [Events & rooms](./events-rooms). --- --- url: 'https://super-line.dogar.biz/guide/events-rooms.md' --- # Events & rooms An **event** is a server→client push where the **server picks the recipients**. Declare it under `serverToClient` (no `subscribe` flag): ```ts serverToClient: { message: { payload: z.object({ room: z.string(), text: z.string(), from: z.string() }) }, } ``` The client listens with `on`, which returns an unsubscribe function: ```ts const off = client.on('message', (m) => render(m)) // m is typed off() // stop listening ``` ## Sending to one connection Inside a handler, push to just that connection with `conn.emit`. It's scoped to the connection's role events: ```ts user: { notify: async (_input, _ctx, conn) => { conn.emit('message', { room: 'lobby', text: 'hi', from: 'system' }) return { ok: true } }, } ``` `conn.emit` is **node-local** — it only reaches that specific socket on this node. To reach a connection or user *wherever they're connected* (across nodes), use `srv.toConn(id).emit(...)` / `srv.toUser(uid).emit(...)` — see [Introspection & presence](./introspection-and-presence#targeted-send-across-nodes). ## Rooms A **room** is a server-controlled group of connections. Add members, then broadcast: ```ts srv.room('room:42').add(conn) // server-controlled membership srv.room('room:42').broadcast('message', { ... }) // delivered to every member srv.room('room:42').remove(conn) srv.room('room:42').size // member count on THIS node ``` Rooms are **mixed-role** — a `user` and an `agent` can be in the same room. Because of that, `broadcast` only accepts **shared events** (the vocabulary every member provably understands). So put events you broadcast to rooms in `shared.serverToClient`: ```ts shared: { serverToClient: { message: { payload: z.object({ room: z.string(), text: z.string(), from: z.string() }) } }, } ``` ::: tip Role-specific fan-out To push a *role-specific* event to a group, use a [topic](./topics) (`forRole(r).publish`) or iterate and `conn.emit`. `room.broadcast` is deliberately shared-only. ::: ## Direct messages Don't stash a `conn` to DM a user — it's node-local. Put each connection in a **per-user room** and broadcast a shared event to it, which works across nodes: ```ts onConnection: (conn, ctx) => srv.room(`user:${ctx.user.id}`).add(conn), // later, from any node: srv.room(`user:${targetId}`).broadcast('dm', { from, text }) ``` ## Event vs topic Use an **event** when the **server** decides who receives it (notifications, room broadcasts, targeted pushes). Use a [**topic**](./topics) when the **client** opts into a stream. Both are `serverToClient`; the only difference is the `subscribe: true` flag and who initiates. ## Events vs the cluster bus Events are server-**chosen** pushes (`conn.emit` / `room.broadcast` / `srv.toConn(id).emit` / `srv.toUser(id).emit`) — the recipient gets them with **no opt-in** and there's **no server-side subscribe**. The [cluster bus](./cluster-event-bus) is the opposite: **opt-in** pub/sub on a shared topic, where any node `server.publish`es and both clients (`client.subscribe`) and other servers (`server.subscribe`) choose to listen. They're different tools — reach for an event when the server decides who's pushed to, and the bus when subscribers opt in and you need cross-node, server-side fan-out. ## Delivery Events are **at-most-once** — a client that's offline misses them (no replay). Design for it: see [Reconnection & delivery](./reconnection-delivery). Next: [Topics](./topics). --- --- url: 'https://super-line.dogar.biz/guide/topics.md' --- # Topics A **topic** is a server→client stream the **client opts into**. Declare it as a `serverToClient` entry with `subscribe: true`: ```ts serverToClient: { prices: { payload: z.object({ symbol: z.string(), price: z.number() }), subscribe: true }, } ``` ## Client: subscribe ```ts const sub = client.subscribe('prices', (p) => render(p)) // p is typed await sub.ready // resolves when the server accepts; rejects if denied/disconnected sub.unsubscribe() ``` `subscribe` returns immediately with a `Subscription`. Await `.ready` if you need to know the subscription was accepted — it **rejects** with `FORBIDDEN` if `authorizeSubscribe` denies it, or `DISCONNECTED` if the socket drops first. On reconnect, topics **auto re-subscribe**. ## Server: publish Topics are **server-publish only** — clients cannot publish. Publish from anywhere on the server: ```ts srv.forRole('user').publish('prices', { symbol: 'AAPL', price: 192.3 }) // role topic srv.publish('announce', { msg: 'maintenance at 5pm' }) // shared topic ``` * A topic in a **role** block is published with `srv.forRole(role).publish(...)` and reaches that role's subscribers. * A topic in the **shared** block is published with `srv.publish(...)` and reaches every role's subscribers. ## Authorizing subscriptions Gate private topics with `authorizeSubscribe` — return `false` or throw to deny (the client's `.ready` rejects `FORBIDDEN`): ```ts import { webSocketServerTransport } from '@super-line/transport-websocket' const srv = createSuperLineServer(api, { transports: [webSocketServerTransport({ server })], authenticate, authorizeSubscribe: async (topic, ctx, conn) => { if (topic.startsWith('org:')) return ctx.user.orgs.includes(topic.slice(4)) return true }, }) ``` Subscribing to a topic that isn't on the connection's role surface is rejected with `NOT_FOUND`. ## Client → others Clients can't publish — that's by design. To let a client fan something out, send a **request**; the handler validates/authorizes, then publishes: ```ts srv.implement({ user: { setPrice: async ({ symbol, price }, ctx) => { if (!ctx.user.canTrade) throw new SuperLineError('FORBIDDEN') srv.forRole('user').publish('prices', { symbol, price }) return { ok: true } }, }, }) ``` ## The cluster bus A shared topic is also a symmetric, **cluster-wide pub/sub bus** — any node `server.publish`es, and both clients (`client.subscribe`) and other servers (`server.subscribe`, in-process and cluster-wide, with local echo) listen. It's a feature in its own right: see **[The cluster event bus](./cluster-event-bus)**. ## Parameterized topics Topics are typed by **exact contract key**. Parameterized names like `room:{id}` aren't type-inferred yet — use a concrete key and carry the id in the payload (filter client-side), or use a [room](./events-rooms) for server-controlled grouping. Next: [The cluster event bus](./cluster-event-bus). --- --- url: 'https://super-line.dogar.biz/guide/cluster-event-bus.md' --- # The cluster event bus A **shared topic** doubles as a symmetric, cluster-wide pub/sub bus — no extra API. One declaration types all three of its subscribers at once: server-side listeners, client-side subscribers, and the publish itself. ```ts defineContract({ shared: { serverToClient: { announce: { payload: z.object({ msg: z.string() }), subscribe: true }, }, }, roles: { /* … */ }, }) ``` Any node publishes; the publish fans out to **three kinds of subscriber** at once: * **same-node `server.subscribe` listeners** — fire directly, in-process, no Redis/WS hop; * **other nodes' `server.subscribe` listeners** — fire via the [adapter](./scaling-adapters) (inbound-validated); * **subscribed clients on any node** — receive over WS with the unchanged `client.subscribe`. ## Server: subscribe `server.subscribe` is the server-side, cluster-wide consumer. It fires for a publish from **any** node — including this one (a **local echo**, delivered in-process with no Redis/WS round-trip). The callback gets `(data, { from })`, where `from` is the origin node id, and it returns an unsubscribe fn: ```ts const off = srv.subscribe('announce', (data, { from }) => { if (from === srv.nodeId) return // self-exclude your own publishes applyAnnounce(data) // converge cluster state }) off() // unsubscribe ``` `data` is typed from the same shared `serverToClient` declaration the client subscribes to. `server.subscribe` is **shared topics only** — role-scoped server-side subscribe is deferred. ## Publish from any node `server.publish` is the same `srv.publish` you already use on shared topics — any node may publish, and every subscriber (server-side and client-side, on every node) sees it: ```ts srv.publish('announce', { msg: 'maintenance at 5pm' }) // shared topic → the bus ``` So one `server.publish` delivers to (1) **same-node** `server.subscribe` listeners in-process, (2) **other nodes'** `server.subscribe` listeners via the adapter, and (3) **subscribed clients** on any node over WS. Role topics still use `srv.forRole(r).publish(...)` and reach that role's **client** subscribers only. ## Validation & isolation Inbound events from **other** nodes are validated against the topic's payload schema; the local echo is trusted (not re-validated). A throwing listener or a bad inbound payload routes to `opts.onError(err, { kind: 'event', name })`, and each listener is **isolated** — one throw never stops the others or the message pump. ::: tip Bus vs. events The bus is **opt-in** pub/sub on a shared topic, with cross-node server-side subscribe. [Events](./events-rooms) (`conn.emit` / `room.broadcast` / `toConn(id).emit` / `toUser(id).emit`) are server-**chosen** pushes — no client opt-in, no server-side subscribe. Both exist; reach for the bus when subscribers opt in, events when the server decides who gets pushed. ::: ## Running it The [`event-bus` example](https://github.com/mertdogar/super-line/tree/main/examples/event-bus) shows the bus in a single process — a `server.publish` fans out to several in-process `server.subscribe` listeners (local echo, no round-trip) plus one client subscriber over WS, no Redis needed. The [`bus-cluster` example](https://github.com/mertdogar/super-line/tree/main/examples/bus-cluster) scales it to three nodes that converge a shared tally — own bumps land in-process via local echo, peers' arrive over the adapter. Next: [Roles & auth](./roles-auth). --- --- url: 'https://super-line.dogar.biz/guide/store.md' --- # Stores A **Store** is super-line's persisted-state primitive: a named, permissioned collection of JSON **Resources** — each a `{ id, accessRules, data }` record. The server is authoritative: it creates Resources, grants and revokes per-client access, and validates every read and write. Clients get a **reactive handle** that catches up to the current value and stays live. Like a [transport](./transports), a Store is **pluggable** and ships as a **server + client pair** you pass at construction. Two implementations ship today — *one plumbing, two consistency models*: * **`@super-line/store-memory`** — last-writer-wins, in-memory, zero-dependency. The default. * [**`@super-line/store-sync`**](./synced-state) — a merging **CRDT** Store (Yjs via [super-store](https://github.com/mertdogar/super-store)). Concurrent writes to different fields converge instead of clobbering — for true multiplayer. Both expose the same `…StoreServer()` / `…StoreClient()` pair, so switching consistency models is a one-line swap — the wire, ACLs, fan-out, and client handle are identical. ::: tip Off-contract by design Unlike requests, events, and topics, a Store is **not** declared in `defineContract`, and its `data` is **not** schema-validated by the server — a CRDT update is an opaque merge delta that can't be validated against a JSON schema anyway. Store `data` is `unknown` end-to-end; you assert its shape. Route anything that needs a hard, typed gate through a normal [request](./requests). (See ADR-0003.) ::: ## Configure the pair Pass matching server and client halves, keyed by name. Each name is an independent backend with its own consistency model and persistence — so one app can mix a CRDT `scene` store and an LWW `config` store. ```ts // server import { memoryStoreServer } from '@super-line/store-memory' const srv = createSuperLineServer(api, { transports: [webSocketServerTransport({ server })], authenticate: (h) => ({ role: 'user' as const, ctx: { uid: h.query.uid } }), identify: (conn) => conn.ctx.uid, // the ACL principal (falls back to conn.id) stores: { docs: memoryStoreServer() }, }) ``` ```ts // client import { memoryStoreClient } from '@super-line/store-memory' const client = createSuperLineClient(api, { transport: webSocketClientTransport({ url }), role: 'user', params: { uid: 'alice' }, stores: { docs: memoryStoreClient() }, // same name as the server }) ``` The client key **must match** the server key — `store('docs')` throws `NOT_FOUND` if the name isn't configured on that side. To go collaborative, swap the pair for the CRDT one; nothing else changes: ```ts import { syncStoreServer } from '@super-line/store-sync' // server: stores: { docs: syncStoreServer() } import { syncStoreClient } from '@super-line/store-sync' // client: stores: { docs: syncStoreClient() } ``` ## Server-authoritative access The server owns Resources. `create` / `grant` / `revoke` / `delete` are **server-side only** — there's no client wire for them, so clients can't create Resources or change access; they read and write only within granted bounds. Access is **deny-by-default**: a principal absent from a Resource's `accessRules` gets nothing. Permissions key off the **principal** — `identify(conn)` (stable across reconnects), or the random `conn.id` when `identify` isn't set. (It's the same hook [presence](./introspection-and-presence) uses.) ```ts const docs = srv.store('docs') await docs.create( 'note-1', { title: 'Draft', body: '' }, { alice: { read: true, write: true }, bob: { read: true, write: false } }, ) await docs.grant('note-1', 'carol', { read: true, write: false }) // open access at runtime await docs.revoke('note-1', 'bob') // remove it await docs.write('note-1', { title: 'Curated', body: '' }) // server co-write (origin 'server') const res = await docs.read('note-1') // Resource | undefined (server admin read, no ACL) const ids = await docs.list() // string[] await docs.delete('note-1') ``` The wire ops a **client** can attempt are read/subscribe and write — each ACL-checked against its principal: | Client attempt | When it fails | Error | |---|---|---| | `open` / `read` a Resource | principal lacks `read` | `FORBIDDEN` | | `write` a Resource | principal lacks `write` | `FORBIDDEN` | | any op on an unknown id | the Resource doesn't exist | `NOT_FOUND` | | `store(name)` | the name isn't configured | `NOT_FOUND` | ## The reactive handle On the client, `open(id)` returns a handle: a snapshot that fills in after catch-up, live updates, and `set` / `update` that write through optimistically. It **re-snapshots automatically on reconnect**. ```ts const note = client.store('docs').open('note-1') await note.ready // catch-up complete; getSnapshot() is undefined until then console.log(note.getSnapshot()) // { title: 'Draft', body: '' } const off = note.subscribe(() => render(note.getSnapshot())) note.update({ title: 'Shipping plan' }) // optimistic locally, fanned to other subscribers note.set({ title: 'Reset', body: '' }) // replace the whole value off() note.close() // drops the server subscription when the last handle for this id closes ``` Writes are **optimistic and fire-and-forget**: the local value changes immediately, then the change is sent up. If the server rejects it (e.g. `FORBIDDEN`), there's no automatic rollback — the rejection is routed to `onStoreError`, and the local replica reconciles on the next remote change or re-seed: ```ts const client = createSuperLineClient(api, { // … stores: { docs: memoryStoreClient() }, onStoreError: (err, { store, id }) => console.warn('write denied', store, id, err), }) ``` For a one-shot read or write with no handle: ```ts const value = await client.store('docs').read('note-1') // Promise await client.store('docs').write('note-1', { title: 'x', body: '' }) // Promise ``` In React, [`useResource`](./react) wraps all of this — open, subscribe, write-through, and close on unmount: ```tsx const { data, set, update } = useResource('docs', 'note-1') // data is undefined until catch-up; set replaces, update merges a partial ``` ## Scaling & observability * **Cross-node.** Each Store declares a clustering mode. `relay` (both stores that ship) is node-local: super-line relays every change across nodes over the [adapter](./scaling-adapters) and converges each node's replica — no extra wiring. A `self` store owns a shared backend (Redis/Postgres) and handles its own cross-node sync; super-line stays out of it. Echo-break — a writer never re-applies its own change — is automatic in both modes. * **Control Center.** With `inspector: true`, store traffic surfaces as `store.write` / `store.grant` / `store.revoke` / `store.subscribe` events under the **Store** filter. The write payload — the only one carrying arbitrary user data — is safe-snapshotted and `inspector.redact`-masked like every other message. (See [Control Center](./control-center).) ## Run it The [`store` example](https://github.com/mertdogar/super-line/tree/main/examples/store) is a permissioned note over the in-memory LWW Store: two users open it, one writes and the other sees it live, a read-only user is denied a write, a third user can't open until the server grants access at runtime, and the server co-writes. ```bash pnpm --filter @super-line/example-store start ``` Next: [Synced state (CRDT)](./synced-state) — the merging Store for true multiplayer. --- --- url: 'https://super-line.dogar.biz/guide/synced-state.md' --- # Synced state (CRDT) The default [Store](./store) is **last-writer-wins**: when two clients edit the same Resource at once, the last write to land wins — and clobbers the other, *even if they touched different fields*. For true multiplayer, reach for **`@super-line/store-sync`**: a [CRDT](https://crdt.tech) Store where concurrent writes **merge** instead of overwriting. It's backed by Yjs (via [super-store](https://github.com/mertdogar/super-store)), but you never touch Yjs directly — the ACLs, the handle, and `useResource` are exactly the same as the LWW store. Only the consistency model changes. ## The one-line swap `store-sync` is the same server + client pair as any [Store](./store) — swap it in by name, change nothing else: ```ts // server — was: stores: { docs: memoryStoreServer() } import { syncStoreServer } from '@super-line/store-sync' stores: { docs: syncStoreServer() } ``` ```ts // client — was: stores: { docs: memoryStoreClient() } import { syncStoreClient } from '@super-line/store-sync' stores: { docs: syncStoreClient() } ``` `syncStoreServer()` takes no options; `syncStoreClient()` takes an optional `{ origin }` — a per-writer id used to break echoes, generated for you if you omit it. ## What merge buys you With LWW, two simultaneous edits race and one loses. With the CRDT store they **converge** — both edits survive on every replica: ```ts // alice and bob open the same doc, then edit at the "same time", each a different field: alice.update({ a: 1 }) bob.update({ b: 2 }) // LWW: whoever lands last wins → { a: 1 } OR { b: 2 } (one edit is lost) // CRDT: both merge everywhere → { a: 1, b: 2 } on alice, bob, and the server ``` Fields nobody touched are preserved, and the document converges to the same value on every node regardless of the order updates arrive in. That's the whole reason to pay for a CRDT. ::: tip How it stays CRDT-agnostic On the wire, a CRDT `update` is an **opaque base64 delta** — super-line relays it without ever parsing the document. The merge logic lives entirely inside the store package, so swapping Yjs for another CRDT would be a store-package change, not a wire change. (See ADR-0002 and ADR-0003.) ::: ## The server as a co-writer The server holds the **canonical** copy of every Resource — which makes it the place to persist state, and lets it edit alongside clients. A server `write` of a partial object **merges** its top-level keys into the document (it doesn't replace it), then fans out to every subscriber tagged `origin: 'server'`: ```ts // a co-writer contributes a field; every other field in the doc is left untouched await srv.store('docs').write('plan', { priority: 5 }) ``` ::: tip Authority is reactive, not preventive A CRDT can't reject *part* of a merge, so the server can't **veto** a client edit — as the hub it can only **react**: observe the merged state and emit a compensating edit. Treat synced-document authority as eventually-consistent last-word correction; route anything that needs a hard gate (money, permissions) through a normal [request](./requests). (See ADR-0003.) ::: ## Catch-up & reconnect `open(id)` seeds the replica from the server's current canonical state — the full CRDT document, sent once — then merges live deltas on top. On reconnect the handle **re-seeds automatically**. Like events and topics, live delivery is **at-most-once**: a client that was offline misses the deltas it didn't receive and recovers by re-snapshotting on reconnect (which the handle does for you). ## In React Nothing changes from the LWW store — [`useResource`](./react) gives you the merged value and a write-through `set` / `update`: ```tsx const { data, set } = useResource('docs', 'plan') if (data === undefined) return

connecting…

// every edit merges live across tabs; concurrent edits to different fields both survive return ``` ## Run it The [`store-sync-json` example](https://github.com/mertdogar/super-line/tree/main/examples/store-sync-json) is a collaborative JSON editor over the CRDT Store: a [`@visual-json`](https://visual-json.dev) editor bound to one shared Resource via `useResource`. Open it in two tabs (or add `?name=bob`), edit any field, and watch edits merge live — concurrent edits to *different* fields both survive. Hit **Server nudge** to see the server co-write a field. ```bash pnpm --filter @super-line/example-store-sync-json dev # http://localhost:5273 ``` ## Roll your own (without the Store seam) `store-sync` is the batteries-included path. If you'd rather own the wire — custom rooms, your own message shapes, no Store abstraction — super-line is also a fine **transport** for a CRDT you drive yourself. Keep a CRDT document per room and relay its opaque update bytes over a shared event: the bus never parses the document, and the server holds the canonical copy (so it can persist and co-write). This is, in effect, what the CRDT Store does for you under the hood. Three messages carry it: a `joinDoc` request that returns the current state to catch up, a `pushUpdate` request for local edits, and a shared `update` event to fan merges out — with an `origin` tag to break the echo. ```ts defineContract({ shared: { serverToClient: { update: { payload: z.object({ docId: z.string(), update: z.string(), origin: z.enum(['peer', 'server']) }) }, }, }, roles: { user: { clientToServer: { joinDoc: { input: z.object({ docId: z.string() }), output: z.object({ snapshot: z.string() }) }, pushUpdate: { input: z.object({ docId: z.string(), update: z.string() }), output: z.object({ ok: z.boolean() }) }, }, }, }, }) ``` On the server, materialize one document per room and make the doc's own update observer the single fan-out + persist point — it fires for **both** client merges and the server's own edits, so the server co-writes just by mutating the doc. On the client, push only locally-originated updates and apply everything else. (CRDT updates are binary and super-line's default serializer is JSON, so base64-wrap them — `btoa` / `atob` are global in the browser and modern Node.) The [`synced-canvas-yjs`](https://github.com/mertdogar/super-line/tree/main/examples/synced-canvas-yjs) and [`synced-canvas-automerge`](https://github.com/mertdogar/super-line/tree/main/examples/synced-canvas-automerge) examples implement this end to end — a collaborative canvas where tabs *and* the server co-edit one document, with a debug panel logging each patch by origin. (The contract above is the Yjs example's; the Automerge one ships an array of change blobs per edit instead of a single update — but the relay pattern is identical, because super-line never parses the bytes either way.) ```bash pnpm --filter @super-line/example-synced-canvas-yjs dev # Yjs pnpm --filter @super-line/example-synced-canvas-automerge dev # Automerge ``` Next: [Roles & auth](./roles-auth). --- --- url: 'https://super-line.dogar.biz/guide/roles-auth.md' --- # Roles & auth A connection's **role** is resolved once, when the connection is established, and fixed for its lifetime. It decides which surface and which `ctx` the connection gets — and it's enforced server-side. ## authenticate returns `{ role, ctx }` `authenticate` runs as the connection opens. It receives the **handshake** (`{ transport, headers, query, peer?, raw }`) — read query params via `h.query.X` and headers via `h.headers`, regardless of which transport carried the connection. Return `{ role, ctx }`, or `throw` to reject (no connection is opened): ```ts import { webSocketServerTransport } from '@super-line/transport-websocket' const srv = createSuperLineServer(api, { transports: [webSocketServerTransport({ server })], authenticate: async (h) => { const token = h.query.token const user = await verifyJwt(token) // throw -> rejected return { role: 'user' as const, ctx: { user } } }, }) ``` `@super-line/transport-websocket` provides the WebSocket transport. Other transports (HTTP/SSE, libp2p) are available — see the Transports guide; they all hand `authenticate` the same `Handshake`. Return `role` as a **literal** (`'user' as const`) so it's inferred as a role key rather than widening to `string`. ## Per-role ctx Different roles usually carry different identity data. Return a discriminated `{ role, ctx }` and each handler block sees the right `ctx`: ```ts authenticate: (h) => { const u = verify(h) return u.role === 'admin' ? { role: 'admin' as const, ctx: { adminId: u.id } } : { role: 'user' as const, ctx: { userId: u.id } } } srv.implement({ admin: { /* ctx is { adminId: string } */ }, user: { /* ctx is { userId: string } */ }, }) ``` In a `shared` handler, `ctx` is the union of all roles' ctx — use common fields, or branch on `conn.role`. ## The role is a claim — verify it The client passes its `role` to `createSuperLineClient`; it's surfaced to `authenticate` on the handshake (`h.query.role` for the WS/HTTP transports) so `authenticate` can read it. **It's a claim, not a fact** — always verify it against the credential: ```ts authenticate: (h) => { const u = verify(tokenFrom(h)) const claimed = h.query.role if (u.role !== claimed) throw new SuperLineError('FORBIDDEN', 'role not granted') return { role: u.role, ctx: { user: u } } } ``` ## Enforcement: NOT\_FOUND Dispatch resolves a handler by `conn.role`, so a request or subscribe outside `shared ∪ roles[conn.role]` resolves to nothing and is rejected with **`NOT_FOUND`** — even if a client hand-crafts the frame to bypass its typed surface. `NOT_FOUND` (rather than `FORBIDDEN`) is deliberate: it doesn't reveal that the method exists for some *other* role. ## AI agents as a role Roles shine when a server serves **both humans and AI agents**. Give each its own verbs and topics: ```ts roles: { user: { clientToServer: { say: {…} } }, agent: { clientToServer: { reportResult: {…} }, serverToClient: { taskAssigned: { payload: z.object({ taskId: z.string(), prompt: z.string() }), subscribe: true } }, }, } ``` * An agent client (`role: 'agent'`) sees only the agent surface — it can `reportResult` and `subscribe('taskAssigned')`, but `agent.say(...)` won't compile. * A user can't call agent-only methods (compile error, and `NOT_FOUND` at runtime). * Each gets its own `ctx` (`{ userId }` vs `{ agentId, capabilities }`). The [chat example](https://github.com/mertdogar/super-line/tree/main/examples/chat) shows a human and an AI agent sharing one room. Next: [Middleware & lifecycle](./middleware-lifecycle). --- --- url: 'https://super-line.dogar.biz/guide/middleware-lifecycle.md' --- # Middleware & lifecycle ## Middleware Middleware runs **before** request and subscribe handlers — for rate-limiting, per-operation authz, logging, and metrics. It's a flat chain: call `next()` to proceed, or `throw` to short-circuit (rejecting the operation). ```ts import { webSocketServerTransport } from '@super-line/transport-websocket' const srv = createSuperLineServer(api, { transports: [webSocketServerTransport({ server })], authenticate, use: [ async (ctx, info, next) => { rateLimit(info.conn.role, info.name) // throw to reject await next() }, async (_ctx, info, next) => { const t = Date.now() await next() metric(info.name, Date.now() - t) // wrap the handler for timing }, ], }) ``` The `info` argument is `{ kind: 'request' | 'subscribe', name, conn }` — so the same chain can gate both requests and topic subscribes: ```ts async (_ctx, info, next) => { if (info.kind === 'subscribe' && info.name === 'feed') throw new SuperLineError('FORBIDDEN') await next() } ``` Middleware does **not** change `ctx`'s type — it's a cross-cutting gate, not a context transformer. `ctx` is the union of role ctxs; branch on `info.conn.role` if you need to narrow. ## Lifecycle hooks ```ts createSuperLineServer(api, { transports: [webSocketServerTransport({ server })], authenticate, onConnection: (conn, ctx) => log('joined', conn.role), onDisconnect: (conn, ctx, code) => cleanup(conn), // code = WebSocket close code onError: (err, info) => report(err, info), // any throw in middleware/handlers }) ``` * **`onConnection`** — once per accepted connection. A handy place to add a connection to a per-user room. * **`onDisconnect`** — when the socket closes; receives the close `code`. * **`onError`** — every error thrown in middleware/handlers, *after* the client has been replied to. Great as a test seam and for centralized reporting. These hooks are also the recommended seams for [testing](./testing) — capture the server-side `conn`, assert connects/disconnects, observe thrown errors. Next: [Error handling](./errors). --- --- url: 'https://super-line.dogar.biz/guide/errors.md' --- # Error handling super-line carries errors end-to-end as a typed `SuperLineError`. Throw one from a handler and the client's promise rejects with the **same code**. ```ts import { SuperLineError } from '@super-line/core' // server send: async ({ room }, ctx) => { if (!ctx.canPost(room)) throw new SuperLineError('FORBIDDEN', 'not a member', { room }) // ... } // client try { await client.send({ room, text }) } catch (e) { if (e instanceof SuperLineError && e.code === 'UNAUTHORIZED') relogin() } ``` A `SuperLineError` has a `code`, an optional human-readable `message`, and optional structured `data` (delivered to the client). ## Codes | Code | Meaning | | --- | --- | | `BAD_REQUEST` | Malformed request; also used for an aborted call. | | `UNAUTHORIZED` | Not authenticated. | | `FORBIDDEN` | Authenticated but not allowed (e.g. a denied subscribe). | | `NOT_FOUND` | Unknown method/topic, or one outside the connection's role surface. | | `TIMEOUT` | The request exceeded its timeout. | | `VALIDATION` | Inbound payload failed schema validation. | | `DISCONNECTED` | The socket dropped (in-flight requests reject with this). | | `INTERNAL` | An unexpected/unknown server error. | You can also use **custom string codes** — autocomplete keeps the built-in set while allowing your own: ```ts throw new SuperLineError('RATE_LIMITED', 'slow down', { retryAfter: 5 }) ``` ## What the client sees * **Expected failures** — `throw new SuperLineError(code, ...)` from a handler; the client gets that exact `code` (and `data`). * **Unexpected throws** — any non-`SuperLineError` thrown becomes `INTERNAL`, so server internals (stack traces, messages) are never leaked. Use [`onError`](./middleware-lifecycle#lifecycle-hooks) to log the real error server-side. * **Validation** — bad inbound input rejects with `VALIDATION` before your handler runs. ## Don't return error sentinels Return values are for success; failures are thrown. This keeps the client's `await` ergonomic (`try/catch`, not result-checking) and the types clean. ```ts // ❌ return { error: 'nope' } // ✅ throw new SuperLineError('FORBIDDEN', 'nope') ``` Next: [Reconnection & delivery](./reconnection-delivery). --- --- url: 'https://super-line.dogar.biz/guide/introspection-and-presence.md' --- # Introspection & presence super-line gives you two views of who's connected, plus the ability to reach and even **request** any connection across nodes. * **Local** (`srv.local`) — synchronous, reads only the connections on *this* node. * **Cluster** (`srv.cluster`) — asynchronous, reads a shared **presence registry** (in-memory for one node, Redis across many). ## Connection metadata Every connection carries identity and liveness: ```ts conn.id // server-assigned unique id (stable for the connection's life) conn.role // its role conn.connectedAt // Date.now() at the upgrade conn.lastPongAt // last heartbeat pong (liveness) — node-local conn.lastPingAt // last heartbeat ping sent ``` ## Local introspection (sync) ```ts srv.nodeId // this process's stable id srv.local.connections // Conn[] on this node srv.local.rooms // room names with members here srv.local.topics // topic names with subscribers here srv.room('lobby').connections // members of a room (this node) srv.room('lobby').size // member count (this node) ``` Everything else is plain JavaScript — filter the array: ```ts srv.local.connections.filter((c) => c.role === 'user') srv.local.connections.find((c) => c.id === someId) const stale = srv.local.connections.filter((c) => Date.now() - (c.lastPongAt ?? 0) > 60_000) ``` ## Heartbeat One timer pings every connection (default every 30s), updating `lastPingAt`/`lastPongAt`. Optionally **reap** dead sockets: ```ts import { webSocketServerTransport } from '@super-line/transport-websocket' createSuperLineServer(api, { transports: [webSocketServerTransport({ server })], authenticate, heartbeat: { interval: 30_000, maxMissed: 2 }, // terminate after 2 missed pongs (fires onDisconnect) }) // heartbeat: false // disable entirely ``` A reaped connection is `terminate()`d and flows through `onDisconnect` like any other drop. ## Cluster introspection (async) The cluster view reads the **presence registry**. To make connections identifiable across nodes, give the server an `identify` hook (a stable user key) and, optionally, a `describeConn` projector for extra fields. `ctx` is **never** auto-serialized. ```ts createSuperLineServer(api, { transports: [webSocketServerTransport({ server })], authenticate, identify: (conn) => conn.ctx.userId, // powers byUser / isOnline / toUser describeConn: (conn) => ({ plan: conn.ctx.plan }), // extra descriptor fields }) await srv.cluster.count() // total connections cluster-wide await srv.cluster.connections() // ConnDescriptor[] across all nodes await srv.cluster.byUser('u42') // that user's connections (any node) await srv.cluster.room('lobby') // members of a room across nodes await srv.cluster.topology() // [{ nodeId, connections, rooms, alive }] await srv.isOnline('u42') // is this user connected anywhere? ``` A `ConnDescriptor` is a **serializable snapshot** (not a live `Conn`): `{ id, role, nodeId, connectedAt, userId?, rooms, ...describeConn }`. It's taken at connect, so seed any descriptor fields in `onConnection` (which runs just before the snapshot). Live-updating values like `lastPongAt` stay node-local and are **not** in the registry. ::: tip Requires a presence-capable adapter The in-memory and Redis adapters both implement presence. `srv.cluster.*` throws a clear error on an adapter that doesn't. Liveness is tracked by a per-node key with a TTL refreshed by the heartbeat, so a **crashed** node's connections drop out of cluster queries automatically; a graceful `close()` removes them immediately. ::: ## Targeted send across nodes Reach a specific connection or user no matter which node holds them — no registry lookup on the delivery path: ```ts srv.toConn(id).emit('notice', { text: 'hi' }) // one connection, any node srv.toUser('u42').emit('notice', { text: 'hi' }) // every device of a user, any node srv.toConn(id).close() // kick one connection (any node) srv.toUser('u42').disconnect() // kick all of a user's connections ``` `emit` here takes **shared** events (the vocabulary every role understands), like `room.broadcast`. This is the cross-node answer to node-local `conn.emit`. ## Server → client requests A connection can be **asked** a typed question. Declare a `serverToClient` entry with `input` + `output` — a third flavor alongside events and topics: ```ts shared: { serverToClient: { confirm: { input: z.object({ q: z.string() }), output: z.object({ ok: z.boolean() }) }, }, } ``` The client answers with `implement` (throw a `SuperLineError` for a typed failure): ```ts client.implement({ confirm: async ({ q }) => ({ ok: q === 'ready?' }), }) ``` The server awaits the reply — across nodes — with `toConn(id).request`: ```ts const answer = await srv.toConn(id).request('confirm', { q: 'ready?' }, { timeout: 5_000 }) answer.ok // boolean, typed ``` `request` is offered on `toConn` only (a single, unambiguous target) and is typed to **shared** server requests — the caller has an id, not a role. If no live node owns the id, or the client doesn't answer in time, it rejects with a `TIMEOUT` `SuperLineError`. To request a *user*, pick a connection first: `const [c] = await srv.cluster.byUser(uid); await srv.toConn(c.id).request(...)`. ## Per-connection state Need mutable scratch state on a connection? Declare a `data` schema in a role block; `conn.data` is typed per role and starts `{}`: ```ts roles: { user: { data: z.object({ lastSeenMsgId: z.number() }), clientToServer: { ack: { input: z.object({ id: z.number() }), output: z.object({}) } }, }, } srv.implement({ user: { ack: async ({ id }, _ctx, conn) => { conn.data.lastSeenMsgId = id // typed return {} }, }, }) ``` ## Backpressure Guard nodes against slow consumers: ```ts createSuperLineServer(api, { transports: [ webSocketServerTransport({ server, backpressure: { maxBufferedBytes: 8 * 1024 * 1024, onExceed: 'close' }, // or 'drop' }), ], authenticate, }) ``` When a connection's `ws.bufferedAmount` exceeds the limit, `'close'` (default) drops it with code `1013`; `'drop'` skips the frame (logged, never silent). Next: [Scaling & adapters](./scaling-adapters). --- --- url: 'https://super-line.dogar.biz/guide/control-center.md' --- # Control Center One screen for your entire cluster: which node owns which socket, who's in which room, the live contract, and every message crossing the bus in real time. No `console.log`s, no extra instrumentation — flip one option and point the **Control Center** at any node. It ships as `@super-line/control-center` and runs with `npx` — no install. ## Enable the inspector The Control Center connects over a reserved WebSocket subprotocol (`superline.inspector.v1`). Turn it on per node — the inspector is server-authoritative, so set `inspector: true` both on the server opts (gates the `msg.*` telemetry) and on the WebSocket transport (negotiates the subprotocol). It is **off by default**: ```ts import { createSuperLineServer } from '@super-line/server' import { webSocketServerTransport } from '@super-line/transport-websocket' const srv = createSuperLineServer(contract, { transports: [webSocketServerTransport({ server, inspector: true })], authenticate, inspector: true, }) ``` Inspector connections **bypass `authenticate`**, are **read-only**, and are kept out of presence, the heartbeat, and `local`/`cluster` results — so the observer never shows up in what it observes. ::: warning Dev / trusted-network only The inspector channel is unauthenticated in v1. Never enable `inspector: true` on an internet-facing production node. (A `redact` option lets you hide specific `ctx`/`data` field names: `inspector: { redact: ['token'] }`.) ::: ## Run it ```sh npx @super-line/control-center --url ws://localhost:3000 ``` This serves the app on a local port and opens your browser. Change the endpoint from the Settings page at any time, or pass `--port` to pick the local port. ## The views ### Topology A hub-and-spoke graph of the whole cluster. The `Adapter · bus` sits at the center (multi-node clusters only — nodes have no direct sockets, they coordinate through the bus), server nodes around it, and each connection around its owning node. Connections are colored by role; selecting a room highlights its members across every node. The graph updates on every live event. ### Connections A table of every connection cluster-wide — role, id, user, owning node, rooms, and uptime. Click a row to open its descriptor plus a best-effort, **node-local** snapshot of `ctx` and `conn.data`. A connection owned by another node shows descriptor-only — point the Control Center at that node to read its `ctx`. ### Contract The full contract surface: `shared` and each role, split by direction with a flavor badge (`request` / `event` / `topic`) and an expandable best-effort JSON Schema per message. ### Live feed Lifecycle churn — `connect` / `disconnect` / `room.add` / `room.remove` / `topic.sub` / `topic.unsub` — published on a reserved channel and fanned out cluster-wide via your Adapter, so an inspector on any one node sees events from every node. It also streams **message traffic** — `msg.request` / `msg.response` / `msg.broadcast` / `msg.publish` / `msg.event`, plus `msg.serverRequest` / `msg.serverReply` between nodes. Filter by **Lifecycle**, **Requests**, or **Events**, pause the stream, and expand any row to inspect its payload (redacted per your `inspector.redact` config). ### Settings Point the Control Center at a different node. The inspector-connection URL is saved to your browser and reused next time; the status dot shows the live socket state. ## How it works Everything rides the WebSocket transport super-line already owns — the Control Center is itself a super-line-style client on the reserved channel. Contract structure comes from the in-process contract object; field-level schemas use the optional [`@standard-community/standard-json`](https://github.com/standard-community/standard-json) bridge when present, falling back to structure-only otherwise. Cluster reads come from the [presence registry](/guide/introspection-and-presence); live events — both lifecycle and message traffic — reuse the same Adapter pub/sub fan-out as rooms and topics, so a single inspector sees the whole cluster. --- --- url: 'https://super-line.dogar.biz/guide/reconnection-delivery.md' --- # Reconnection & delivery The client is resilient by default — but the delivery model is **at-most-once**, and designing for that is the key to correct realtime apps. ## What the client does automatically * **Auto-reconnect** with exponential backoff + full jitter. Configurable via `reconnectBaseMs` (500), `reconnectMaxMs` (30000), `reconnectFactor` (2); set `reconnect: false` to disable. * **Topics auto re-subscribe** on reconnect — you don't re-call `subscribe`. * **In-flight requests reject** with `DISCONNECTED` when the socket drops. * **Calls made *while* reconnecting are queued** and flushed once the connection is back. ```ts import { webSocketClientTransport } from '@super-line/transport-websocket' const client = createSuperLineClient(api, { transport: webSocketClientTransport({ url }), role: 'user', reconnect: true, reconnectBaseMs: 500, reconnectMaxMs: 30_000, reconnectFactor: 2, }) ``` The backoff math is exported as a pure function, `backoffDelay(attempt, opts)`, if you want to reuse or test it. ## At-most-once delivery Messages sent while a client is offline are **not replayed**. This is the right default for cursors, presence, and live prices — stale state is worse than missed state. It means you should: * **Make handlers idempotent.** A client may retry after a reconnect; the same request arriving twice shouldn't double-charge. * **Re-run join flows after reconnect.** Rooms are *server-controlled*, so they aren't auto-restored — re-call your `join` request when the connection comes back. (Topics, which are *client-controlled*, do auto re-subscribe.) * **Don't assume in-flight requests survive a drop.** They reject `DISCONNECTED`; decide per call whether to retry. Session resume/replay is not built yet — see the project status in the README. ## The 401-looks-like-a-drop caveat Over the WebSocket API, a rejected upgrade (e.g. bad credentials) is indistinguishable from any other drop. So a client with bad credentials and `reconnect: true` will **retry forever**. When you want an auth failure to surface immediately (tests, login flows), set `reconnect: false`: ```ts const client = createSuperLineClient(api, { transport: webSocketClientTransport({ url }), role: 'user', params: { token: 'bad' }, reconnect: false, }) await client.whoami({}) // rejects DISCONNECTED right away instead of retrying ``` Next: [Serialization](./serialization). --- --- url: 'https://super-line.dogar.biz/guide/serialization.md' --- # Serialization Every frame is encoded by a `Serializer`. The default is JSON; you can swap in something richer. The server and client **must use the same serializer**. ## The default: JSON `jsonSerializer` (the default) is fast and universal, but JSON has no notion of `Date`, `Map`, `Set`, `BigInt`, etc. A `Date` you send becomes a **string** on the other end: ```ts serverToClient: { tick: { payload: z.object({ at: z.date() }) } } // ❌ z.date() will fail to validate the string JSON produced ``` Two ways to handle dates with JSON: ```ts // 1. coerce on the receiving schema { at: z.coerce.date() } // accepts the ISO string and parses it back to a Date // 2. send a number and convert yourself { at: z.number() } // Date.now() on the way out, new Date(at) on the way in ``` ## Richer types: superjson To preserve `Date`, `Map`, `Set`, `BigInt`, etc. transparently, plug in a `superjson`-backed serializer on **both** ends: ```ts import superjson from 'superjson' import type { Serializer } from '@super-line/core' import { webSocketServerTransport, webSocketClientTransport } from '@super-line/transport-websocket' const serializer: Serializer = { encode: (v) => superjson.stringify(v), decode: (d) => superjson.parse(typeof d === 'string' ? d : new TextDecoder().decode(d)), } createSuperLineServer(api, { transports: [webSocketServerTransport({ server })], authenticate, serializer }) createSuperLineClient(api, { transport: webSocketClientTransport({ url }), role: 'user', serializer }) // MUST match ``` ## Custom serializers A `Serializer` is just: ```ts interface Serializer { encode(value: unknown): string | Uint8Array decode(data: string | Uint8Array): unknown } ``` Returning a `Uint8Array` lets you use a binary format (e.g. msgpack). As always, both ends must agree. ::: warning Validation still runs Validation happens **after** decode, against your schemas. So even with `superjson`, your schema must describe the decoded shape (a real `Date`, for `superjson`; a string/number, for JSON). ::: Next: [Scaling & adapters](./scaling-adapters). --- --- url: 'https://super-line.dogar.biz/guide/react.md' --- # React `@super-line/react` binds typed hooks to a contract + role. Create the hooks once, create the client once, wrap your tree in the `Provider`. ```tsx import { useState } from 'react' import { createSuperLineClient } from '@super-line/client' import { webSocketClientTransport } from '@super-line/transport-websocket' import { createSuperLineHooks } from '@super-line/react' import { chat } from './contract' const { Provider, useClient, useRequest, useEvent, useSubscription } = createSuperLineHooks() function Root() { // create the client once; it connects immediately and reconnects on its own const [client] = useState(() => createSuperLineClient(chat, { transport: webSocketClientTransport({ url: 'ws://localhost:3000' }), role: 'user', params: { name: 'ada' }, })) return ( ) } ``` The role is a type argument to `createSuperLineHooks()`, so every hook is narrowed to that role's surface. ## The hooks ```tsx function Room({ room }: { room: string }) { // useRequest: { data, error, isLoading, call } const { call: send, isLoading } = useRequest('send') // useSubscription: latest topic value (or undefined before the first message) const presence = useSubscription('presence') // useEvent: run a handler on each pushed event useEvent('message', (m) => append(m)) // useClient: the raw client, if you need it const client = useClient() const onSubmit = (text: string) => send({ room, text }).catch(() => {}) // ... } ``` * **`useRequest(method)`** → `{ data, error, isLoading, call }`. `call(input)` performs the typed request and updates state; it also returns the promise. * **`useSubscription(topic)`** → the latest value, re-rendering as new ones arrive. Subscribes on mount, unsubscribes on unmount. * **`useEvent(event, handler)`** → invokes `handler` for each pushed event (the latest handler is always used; no stale closures). * **`useClient()`** → the underlying `SuperLineClient`. ## StrictMode In development, React StrictMode double-invokes effects, which would open/close the live socket twice. The [react-chat example](https://github.com/mertdogar/super-line/tree/main/examples/react-chat) omits StrictMode for that reason; add it back once you guard the client lifecycle (e.g. a ref-counted singleton). Next: [Testing](./testing). --- --- url: 'https://super-line.dogar.biz/guide/scaling-adapters.md' --- # Choose your backbone A single super-line server uses an in-memory adapter — rooms and topics fan out within that one process. To run **more than one process** (behind a load balancer), give every server a **shared adapter** so fan-out crosses nodes. The adapter is the *server↔server* axis. (The *client↔server* wire is the separate, independent [transport](./transports) axis — you pick each on its own.) ## The adapter seam Rooms, topics, and the [cluster event bus](./cluster-event-bus) all compile down to channel pub/sub behind the `Adapter` interface. Swap the implementation; the rest of your code is unchanged — only the `adapter:` line differs: ```ts import { createRedisAdapter } from '@super-line/adapter-redis' import { webSocketServerTransport } from '@super-line/transport-websocket' const srv = createSuperLineServer(api, { transports: [webSocketServerTransport({ server })], authenticate, adapter: createRedisAdapter('redis://localhost:6379'), // ← the only line that changes }) ``` Point every server process at the same backbone, and `room.broadcast`, `srv.publish` / `forRole(r).publish`, and the cluster bus all reach clients (and peers) on **any** node. At-most-once delivery is preserved. ::: tip No adapter for a single node You don't need an adapter for one process — the default in-memory adapter handles it. Add a backbone only when you scale out. ::: ## Which adapter? | Adapter | Reach for it when… | Presence | Shape | |---|---|---|---| | **[Redis](./adapter-redis)** | the pragmatic default for a backend cluster | strong, central | one central broker, broker-enforced expiry | | **[RabbitMQ](./adapter-rabbitmq)** | you already run it, or want **selective** per-channel routing | gossip (eventual) | broker delivers only subscribed channels | | **[ZeroMQ](./adapter-zeromq)** | you want the **lightest** broker-less option | gossip (eventual) | direct sockets; mesh O(N²) or proxy mode | | **[libp2p](./adapter-libp2p)** | decentralized **self-hosted / edge** | gossip (eventual) | NAT traversal, encrypted, discovery; ESM-only | All four implement the same `Adapter` seam, so switching is a one-line change. **Redis** is the default; the three broker-less options trade central presence for one fewer service to run. ## Running it The [`scaling` example](https://github.com/mertdogar/super-line/tree/main/examples/scaling) boots a real cluster with Docker Compose — Redis, a Caddy load balancer, three server nodes, and six client containers — so you can watch a publish, a room broadcast, and a shared `stats` topic gossiped over the bus fan out across separate processes: ```bash cd examples/scaling && docker compose up ``` Each broker-less adapter ships the same cluster with the broker swapped out: [`scaling-libp2p`](https://github.com/mertdogar/super-line/tree/main/examples/scaling-libp2p), [`scaling-rabbitmq`](https://github.com/mertdogar/super-line/tree/main/examples/scaling-rabbitmq), and the `react-chat-cluster-*` variants. Next: pick a backbone — [Redis](./adapter-redis) · [libp2p](./adapter-libp2p) · [RabbitMQ](./adapter-rabbitmq) · [ZeroMQ](./adapter-zeromq). --- --- url: 'https://super-line.dogar.biz/guide/adapter-redis.md' --- # Redis adapter The pragmatic default for a backend cluster: a central Redis every node points at. Simple, the strongest presence of any adapter, at-most-once delivery. Provided by `@super-line/adapter-redis`. ```bash pnpm add @super-line/adapter-redis ``` ## Setup Point every server process at the **same** Redis and pass the adapter — nothing else in your code changes: ```ts import { createRedisAdapter } from '@super-line/adapter-redis' import { webSocketServerTransport } from '@super-line/transport-websocket' const srv = createSuperLineServer(api, { transports: [webSocketServerTransport({ server })], authenticate, adapter: createRedisAdapter('redis://localhost:6379'), // ← the only line that changes }) ``` Now `room.broadcast`, `srv.publish` / `forRole(r).publish`, and the [cluster event bus](./cluster-event-bus) reach clients (and peers) on **any** node. ## Behavior notes * **Strong, central presence.** `srv.cluster.*` / `srv.isOnline` are backed by Redis keys with **broker-enforced expiry** — a crashed node's connections clear on TTL, and a graceful shutdown clears promptly. This is the most authoritative presence of any adapter. * **At-most-once delivery**, matching the rest of the library (transient pub/sub, no replay). * One central service to run and coordinate — when you'd rather go broker-less, see [libp2p](./adapter-libp2p) or [ZeroMQ](./adapter-zeromq). ::: tip No adapter for a single node One process uses the default in-memory adapter — add Redis only when you scale out behind a load balancer. ::: Run it: the [`scaling`](https://github.com/mertdogar/super-line/tree/main/examples/scaling), [`react-chat-cluster`](https://github.com/mertdogar/super-line/tree/main/examples/react-chat-cluster), and [`bus-cluster`](https://github.com/mertdogar/super-line/tree/main/examples/bus-cluster) examples all run on Redis. Next: [libp2p](./adapter-libp2p) · back to [Choose your backbone](./scaling-adapters). --- --- url: 'https://super-line.dogar.biz/guide/adapter-libp2p.md' --- # libp2p adapter Broker-less and decentralized: nodes peer directly over a [libp2p](https://libp2p.io) gossipsub mesh — no Redis, no central service. The heavyweight decentralized option (NAT traversal, encrypted transports, peer discovery) for self-hosted / edge deployments. Provided by `@super-line/adapter-libp2p`. ```bash pnpm add @super-line/adapter-libp2p ``` ::: tip Adapter, not transport This is the *server↔server* fan-out adapter. For *client↔server* libp2p/WebRTC connections, see the [libp2p transport](./transport-libp2p) — they're independent, and you can use either, both, or neither. ::: ## Setup Server code is unchanged; only the adapter line differs: ```ts import { createLibp2pAdapter } from '@super-line/adapter-libp2p' import { webSocketServerTransport } from '@super-line/transport-websocket' const adapter = await createLibp2pAdapter({ listen: ['/ip4/0.0.0.0/tcp/9001'], bootstrap: ['/dns4/seed-1/tcp/9001/p2p/12D3Koo…'], // seed multiaddrs identity: { path: '/var/lib/app/p2p' }, // stable peer ID across restarts }) const srv = createSuperLineServer(api, { transports: [webSocketServerTransport({ server })], authenticate, adapter, }) ``` The factory is **async** — it starts the node and joins the gossipsub mesh before returning a ready adapter. ## Behavior notes * It fans out rooms, topics, and the [cluster event bus](./cluster-event-bus) the same way; a gossip-replicated directory backs `srv.cluster.*` / `srv.isOnline`. * **Trade-off vs. Redis:** broker-less and decentralized, at the cost of **eventually-consistent presence** and **best-effort delivery** (no central store). * **ESM-only** (libp2p is ESM-only). * Run **≥2 stable seed nodes** and **persist their identity** (`identity.path`) so bootstrap lists stay valid across restarts. Run it: the [`scaling-libp2p`](https://github.com/mertdogar/super-line/tree/main/examples/scaling-libp2p) and [`react-chat-cluster-libp2p`](https://github.com/mertdogar/super-line/tree/main/examples/react-chat-cluster-libp2p) examples are the Redis clusters with the broker **deleted** — the nodes peer over libp2p instead. Next: [RabbitMQ](./adapter-rabbitmq) · back to [Choose your backbone](./scaling-adapters). --- --- url: 'https://super-line.dogar.biz/guide/adapter-rabbitmq.md' --- # RabbitMQ adapter For teams already running RabbitMQ, or who want the broker to do **selective routing** — each node receives only the channels it actually has members for. Provided by `@super-line/adapter-rabbitmq`. ```bash pnpm add @super-line/adapter-rabbitmq ``` ## Setup ```ts import { createRabbitmqAdapter } from '@super-line/adapter-rabbitmq' import { webSocketServerTransport } from '@super-line/transport-websocket' const adapter = await createRabbitmqAdapter('amqp://localhost:5672') const srv = createSuperLineServer(api, { transports: [webSocketServerTransport({ server })], authenticate, adapter, }) ``` The factory is **async** — it connects and declares its topology before returning a ready adapter. ## How it routes Channels become routing keys on one durable `direct` exchange. Each node owns an exclusive, auto-delete queue and binds **only the channels it has local members for** — so the broker delivers each message only to the nodes that subscribed. Built on [`rabbitmq-client`](https://www.npmjs.com/package/rabbitmq-client), so a dropped connection auto-reconnects and the node's bindings are replayed. ## Behavior notes * **Gossip-replicated presence** (eventually consistent, like libp2p): RabbitMQ has no shared key-value store, so presence rides the same exchange. A crashed node's connections clear after a liveness TTL (~30s by default, vs Redis's broker-enforced key expiry); a graceful shutdown clears promptly. * **At-most-once delivery** (transient messages, no acks, no persistence). * One caveat Redis doesn't have: AMQP **routing keys cap at 255 bytes**, so a channel name (embedding room / user / topic) longer than that is rejected with a clear error. Run it: the [`scaling-rabbitmq`](https://github.com/mertdogar/super-line/tree/main/examples/scaling-rabbitmq) example boots the cluster with the management UI exposed, so you can watch the exchange, per-node queues, and bindings live. Next: [ZeroMQ](./adapter-zeromq) · back to [Choose your backbone](./scaling-adapters). --- --- url: 'https://super-line.dogar.biz/guide/adapter-zeromq.md' --- # ZeroMQ adapter The lightest way to go broker-less: nodes peer directly over plain [ZeroMQ](https://zeromq.org) sockets — no extra service, and no full libp2p stack. Provided by `@super-line/adapter-zeromq`. ```bash pnpm add @super-line/adapter-zeromq ``` ::: warning ESM-only · native addon ESM-only, and a native addon (Node-only). ::: ## Setup — mesh Bind a `PUB`, connect a `SUB` to every peer. Discovery is just addresses — no registry: ```ts import { createZeroMqAdapter } from '@super-line/adapter-zeromq' import { webSocketServerTransport } from '@super-line/transport-websocket' const adapter = await createZeroMqAdapter({ bind: 'tcp://0.0.0.0:9101', peers: ['tcp://node-2:9101', 'tcp://node-3:9101'], }) const srv = createSuperLineServer(api, { transports: [webSocketServerTransport({ server })], authenticate, adapter, }) ``` ZeroMQ's `connect` is lazy and auto-reconnecting, so nodes can start in any order. ## Larger fleets — proxy mode A static mesh is O(N²) connections. For a larger or dynamic fleet, swap the mesh for a central **forwarder** — one `npx super-line-zeromq-proxy` process — and point nodes at it: ```ts const adapter = await createZeroMqAdapter({ mode: 'proxy', frontendUrl: 'tcp://proxy:5557', // node PUBs connect here backendUrl: 'tcp://proxy:5558', // node SUBs connect here }) ``` ## Behavior notes * Fans out rooms, topics, and the [cluster event bus](./cluster-event-bus) the same way, with a **gossip-replicated** directory backing `srv.cluster.*` / `srv.isOnline` (eventually-consistent, like libp2p — no central store). * **At-most-once delivery**, matching the rest of the library. * Mesh mode is gorgeous at a handful of nodes; reach for **proxy mode (or Redis)** for large / dynamic fleets. Run it: the [`react-chat-cluster-zeromq`](https://github.com/mertdogar/super-line/tree/main/examples/react-chat-cluster-zeromq) example is the Redis chat cluster with the broker **deleted** — same app, one fewer service. Back to [Choose your backbone](./scaling-adapters). --- --- url: 'https://super-line.dogar.biz/guide/testing.md' --- # Testing The most reliable way to test super-line is over a **real loopback server** — boot a server on an ephemeral port and connect a real client, so you exercise the actual handshake, frames, and validation. The lifecycle hooks (`onConnection`/`onDisconnect`/`onError`) double as observation seams. Examples use [Vitest](https://vitest.dev). ## A tiny harness ```ts // test/harness.ts import http from 'node:http' import type { AddressInfo } from 'node:net' import type { Contract, RoleOf } from '@super-line/core' import { createSuperLineServer, type AuthResult, type SuperLineServerOptions, type SuperLineServer } from '@super-line/server' import { createSuperLineClient, type SuperLineClient, type SuperLineClientOptions } from '@super-line/client' import { webSocketServerTransport, webSocketClientTransport } from '@super-line/transport-websocket' export function createHarness() { const cleanups: Array<() => Promise | void> = [] async function server>( contract: C, opts: Omit, 'transports'>, ): Promise<{ srv: SuperLineServer; url: string }> { const httpServer = http.createServer() const srv = createSuperLineServer(contract, { ...opts, transports: [webSocketServerTransport({ server: httpServer })] }) await new Promise((r) => httpServer.listen(0, r)) const url = `ws://127.0.0.1:${(httpServer.address() as AddressInfo).port}` cleanups.push(async () => { await srv.close(); await new Promise((r) => httpServer.close(() => r())) }) return { srv, url } } function client>( contract: C, { url, ...opts }: Omit, 'transport'> & { url: string }, ): SuperLineClient { const c = createSuperLineClient(contract, { ...opts, transport: webSocketClientTransport({ url }) }) cleanups.unshift(() => c.close()) // clients close BEFORE the servers they connect to return c } async function dispose() { for (const fn of cleanups.splice(0)) await fn() } return { server, client, dispose } } export const tick = (ms = 10) => new Promise((r) => setTimeout(r, ms)) export async function waitFor(pred: () => boolean, timeout = 2000) { const start = Date.now() while (!pred()) { if (Date.now() - start > timeout) throw new Error('waitFor timeout'); await tick(5) } } ``` ## Round-trip + typed error ```ts it('round-trips and surfaces typed errors', async () => { const { srv, url } = await h.server(api, { authenticate: () => ({ role: 'user' as const, ctx: {} }) }) srv.implement({ user: { echo: async ({ text }) => ({ text }), boom: async () => { throw new SuperLineError('FORBIDDEN', 'nope') }, }, }) const client = h.client(api, { url, role: 'user' }) expect(await client.echo({ text: 'hi' })).toEqual({ text: 'hi' }) await expect(client.boom({})).rejects.toMatchObject({ code: 'FORBIDDEN' }) }) ``` ## Role enforcement ```ts it('rejects a cross-role call with NOT_FOUND', async () => { const user = h.client(api, { url, role: 'user' }) // bypass the typed surface to prove the runtime boundary const call = (user as unknown as { reportResult: (i: unknown) => Promise }).reportResult({ taskId: 't1' }) await expect(call).rejects.toMatchObject({ code: 'NOT_FOUND' }) }) ``` ## Hooks as seams + simulating a drop `onConnection` captures the server-side `conn`; `conn.terminate()` simulates a network drop: ```ts let last: Conn | undefined const { srv, url } = await h.server(api, { authenticate: () => ({ role: 'user' as const, ctx: {} }), onConnection: (c) => { last = c }, }) srv.implement({ user: { hang: () => new Promise(() => {}) } }) const client = h.client(api, { url, role: 'user', reconnectBaseMs: 10 }) const inflight = client.hang({}) await tick(20) last!.terminate() await expect(inflight).rejects.toMatchObject({ code: 'DISCONNECTED' }) // in-flight rejects ``` ## Cross-node without Redis Share one `MemoryBus` across two servers to simulate nodes — no Docker needed: ```ts import { MemoryBus, createInMemoryAdapter } from '@super-line/server' const bus = new MemoryBus() const a = await h.server(api, { authenticate: () => ({ role: 'user' as const, ctx: {} }), adapter: createInMemoryAdapter(bus) }) const b = await h.server(api, { authenticate: () => ({ role: 'user' as const, ctx: {} }), adapter: createInMemoryAdapter(bus) }) // publish on b, assert a subscriber on a receives it ``` For **real cross-process** tests use `testcontainers` + `createRedisAdapter(url)`, and skip cleanly when Docker is absent (`describe.skipIf`). ## React hooks Render hooks against a real client with `renderHook` (jsdom): ```ts const { Provider, useRequest } = createSuperLineHooks() const wrapper = ({ children }) => createElement(Provider, { client, children }) const { result } = renderHook(() => useRequest('echo'), { wrapper }) await act(async () => { await result.current.call({ text: 'hi' }) }) expect(result.current.data).toEqual({ text: 'hi' }) ``` ## Tips * **Close the client before the server** — an open connection blocks `server.close()` (the harness handles this via `unshift`). * **Return `role` as a literal** (`'user' as const`) so it's inferred as the role key. * `backoffDelay` is a pure function — unit-test it directly, no timers or sockets. * Prefer a small `reconnectBaseMs` + `waitFor` over fake timers — real I/O isn't faked by `vi.useFakeTimers()`. Next: [Comparison & FAQ](./comparison-faq). --- --- url: 'https://super-line.dogar.biz/guide/ai-agents.md' --- # Use with your AI agent super-line ships an **agent guide** — the contract model, the interaction flavors, auth, scaling, testing, and the common pitfalls — so your AI coding agent writes correct super-line code instead of guessing. Install it into your project and your agent picks it up. It comes in two forms: * **Claude Code** gets the full skill — `SKILL.md` (always-loaded trigger) plus `REFERENCE.md` / `RECIPES.md` loaded on demand (progressive disclosure). * **Every other agent** gets a single condensed `AGENTS.md` that points to this site for depth. All of it lives in the public repo under [`skills/super-line/`](https://github.com/mertdogar/super-line/tree/main/skills/super-line) — no extra tooling. Claude's skill is a *folder* (use `degit`); the others are a *single file* (use `curl`). ## Claude Code Copy the skill into your project (or `~/.claude/skills/` to make it global): ```bash npx degit mertdogar/super-line/skills/super-line .claude/skills/super-line ``` It activates automatically when you import from `@super-line/*` or mention super-line — no config needed. ## Cursor Cursor reads rules from `.cursor/rules/*.mdc`. Fetch the condensed guide and prepend Cursor's frontmatter in one step: ```bash mkdir -p .cursor/rules { printf -- '---\ndescription: super-line — typesafe WebSocket contracts (roles + direction)\nalwaysApply: true\n---\n\n' curl -fsSL https://raw.githubusercontent.com/mertdogar/super-line/main/skills/super-line/AGENTS.md } > .cursor/rules/super-line.mdc ``` ## GitHub Copilot Copilot reads `.github/instructions/*.instructions.md`: ```bash mkdir -p .github/instructions { printf -- '---\napplyTo: "**"\n---\n\n' curl -fsSL https://raw.githubusercontent.com/mertdogar/super-line/main/skills/super-line/AGENTS.md } > .github/instructions/super-line.instructions.md ``` ## Any other agent (Windsurf, Cline, Codex, Zed, …) Most newer agents read an `AGENTS.md` at the project root, or can be pointed at any markdown file. Drop the condensed guide in as-is — no frontmatter needed: ```bash curl -fsSL -o AGENTS.md https://raw.githubusercontent.com/mertdogar/super-line/main/skills/super-line/AGENTS.md ``` If your agent uses a different rules path (e.g. `.windsurfrules`, `.clinerules/`), write it there instead (`curl -fsSL -o …`). ## Keeping it current The guide tracks the published API. Re-run the command above to refresh after upgrading super-line. The content mirrors these docs, so an agent that can browse the web can also just be pointed at . > Prefer not to use the terminal? Every file is browsable at [`skills/super-line/`](https://github.com/mertdogar/super-line/tree/main/skills/super-line) — copy its contents into the path your agent expects. ## Point an agent at these docs directly Every page on this site is also published as raw markdown, and the whole site is indexed for LLMs — so you can hand a page (or all of them) to an agent without copying anything by hand. * **Per-page actions.** Each doc page has a **Copy markdown** button (with **View as Markdown**, **Open in ChatGPT**, and **Open in Claude**). "Open in …" sends the agent the page's markdown URL and asks it to read from there. * **Any page as markdown.** Append `.md` to a page's URL — e.g. . * **`llms.txt`** — a machine-readable index of every page, at . * **`llms-full.txt`** — the entire documentation concatenated into one file, at . These follow the [llms.txt](https://llmstxt.org) convention, so an agent that can browse the web can ingest the full docs from a single URL. --- --- url: 'https://super-line.dogar.biz/guide/comparison-faq.md' --- # Comparison & FAQ ## How it compares | | super-line | Socket.IO | tRPC | raw `ws` | | --- | :---: | :---: | :---: | :---: | | Typesafe contract | ✅ | ⚠️ types-only | ✅ | ❌ | | Runtime validation | ✅ | ❌ | ✅ | ❌ | | Per-role contracts | ✅ | ❌ | ❌ | ❌ | | Req/res | ✅ | ack callbacks | ✅ | ❌ | | Rooms | ✅ | ✅ | ❌ | ❌ | | Topics (pub/sub) | ✅ | ⚠️ via rooms | subscriptions | ❌ | | Inter-server messaging | ✅ | ✅ | ❌ | ❌ | | Multi-node | ✅ adapter | ✅ adapter | ❌ | ❌ | | Zero codegen | ✅ | ✅ | ✅ | n/a | ### Why not Socket.IO? Socket.IO splits its types into `ClientToServerEvents` / `ServerToClientEvents` / `InterServerEvents` interfaces you maintain by hand and wire as **positional generics** — `Server` on the server, reversed on the client, so swapping two still compiles. And its types are compile-time only: there's no runtime validation. super-line keeps the same directional split but in **one shared object** (can't misorder, can't drift), validates inbound automatically, and adds something Socket.IO doesn't have: **per-role contracts** — one server giving `user` and `agent` clients distinct, enforced surfaces. ### Why not tRPC? tRPC is excellent for request/response (and SSE subscriptions), but it doesn't model rooms or client-driven pub/sub topics, and it's not built for bidirectional realtime. super-line is purpose-built for it while keeping tRPC-grade end-to-end types. ## FAQ ### Do I need Redis? No. A single node uses the in-memory adapter. Add `@super-line/adapter-redis` only when you run more than one process. See [Scaling & adapters](./scaling-adapters). ### Does the client work in the browser? Yes (and Node 22+). It uses the global `WebSocket`; pass `{ WebSocket }` on older runtimes. ### How are types shared? Put the contract in a module/package both sides import. No build step, no generated files. ### Can clients publish to topics? No — topics are server-publish only. Send a request and have the handler publish. See [Topics → Client → others](./topics#client-others). ### What's the delivery guarantee? At-most-once. Offline clients miss messages (no replay). Make handlers idempotent and re-run join flows after reconnect. See [Reconnection & delivery](./reconnection-delivery). ### How do I document/teach this to an AI agent? The repo ships an [agent skill](https://github.com/mertdogar/super-line/tree/main/skills/super-line) (`SKILL.md` + `REFERENCE.md` + `RECIPES.md`) that teaches AI coding agents the model and best practices. Copy it into your agent's skills directory. ### Is it stable? Pre-1.0. Implemented: role-scoped contracts, req/res, events, rooms, topics, inter-server, auth, reconnect, middleware, in-memory + Redis adapters, React hooks. Not yet: fire-and-forget signals, mutable per-connection state, NATS adapter, session resume/replay, parameterized-topic type inference. --- --- url: 'https://super-line.dogar.biz/reference/@super-line/adapter-libp2p.md' --- [Documentation](../../index.md) / @super-line/adapter-libp2p # @super-line/adapter-libp2p Decentralized, broker-less [libp2p](https://libp2p.io) (gossipsub) adapter for [**super-line**](https://mertdogar.github.io/super-line/) — fan out rooms, topics, the cluster event bus, and cluster presence across multiple server processes with **no central broker**. A drop-in alternative to [`@super-line/adapter-redis`](https://www.npmjs.com/package/@super-line/adapter-redis). ```bash pnpm add @super-line/adapter-libp2p ``` > **ESM-only** — libp2p is ESM-only, so this package ships ESM only (Node 18+, `"type": "module"`). ```ts import { createSuperLineServer } from '@super-line/server' import { webSocketServerTransport } from '@super-line/transport-websocket' import { createLibp2pAdapter } from '@super-line/adapter-libp2p' import { api } from './contract' // builds a libp2p node for you; persist identity so seed peer IDs survive restarts const adapter = await createLibp2pAdapter({ listen: ['/ip4/0.0.0.0/tcp/9001'], bootstrap: ['/dns4/seed-1/tcp/9001/p2p/12D3Koo…'], // seed multiaddrs identity: { path: '/var/lib/app/p2p' }, }) const srv = createSuperLineServer(api, { transports: [webSocketServerTransport({ server })], authenticate, adapter, }) console.log('p2p:', adapter.node.getMultiaddrs().map(String)) // share a seed's address for bootstrap lists ``` Every node joins one shared gossipsub topic; each node filters incoming messages by its local subscriptions, so `subscribe`/`unsubscribe` are local with no network round-trip. At-most-once delivery, matching the library's model. ## How it works * **Fan-out** — one shared gossipsub topic + a small binary envelope. The adapter delivers to its own local members directly (no dependency on gossipsub `emitSelf`). * **Presence** — a gossip-replicated directory (deltas + periodic snapshots, monotonic-seq reconcile, heartbeat/TTL liveness) powers `srv.cluster.*` / `srv.isOnline`. On by default; pass `presence: false` to disable. * **Discovery** — bootstrap-only: dial a fixed list of seed multiaddrs. Run ≥2 seeds, or list every peer for a small cluster. Persist identity (`identity: { path }`) so seed peer IDs are stable. ## Options | Option | Meaning | | --- | --- | | `node` | Bring your own started libp2p node (must expose a gossipsub `pubsub` service). The adapter won't stop a node it didn't create. | | `listen` | Listen multiaddrs for the built-in node (default `/ip4/0.0.0.0/tcp/0`). Seeds need a FIXED port. | | `bootstrap` | Seed multiaddrs (incl. `/p2p/`) to dial on startup. | | `transport` | `'tcp'` (default) or `'ws'`. | | `identity` | A raw `PrivateKey`, `{ path }` to load-or-create a persistent Ed25519 key, or omit for an ephemeral key (warns). | | `presence` | `false` to disable, or `{ snapshotIntervalMs, livenessTtlMs }` to tune. | | `topic` | The shared gossipsub topic (default `'super-line/v1'`). | * 📖 Docs: * 📚 Guide: [scaling & adapters](https://mertdogar.github.io/super-line/guide/scaling-adapters) * 🧩 Example: [`scaling-libp2p`](https://github.com/mertdogar/super-line/tree/main/examples/scaling-libp2p) * 🧩 Source: MIT © Mert ## Interfaces * [Libp2pAdapterOptions](interfaces/Libp2pAdapterOptions.md) ## Type Aliases * [PubSubLibp2p](type-aliases/PubSubLibp2p.md) ## Functions * [createLibp2pAdapter](functions/createLibp2pAdapter.md) --- --- url: >- https://super-line.dogar.biz/reference/@super-line/adapter-libp2p/interfaces/Libp2pAdapterOptions.md --- [Documentation](../../../index.md) / [@super-line/adapter-libp2p](../index.md) / Libp2pAdapterOptions # Interface: Libp2pAdapterOptions Defined in: [index.ts:19](https://github.com/mertdogar/super-line/blob/130bd9adcf4898b57c097e3121e358d596527fa2/packages/adapter-libp2p/src/index.ts#L19) Options for [createLibp2pAdapter](../functions/createLibp2pAdapter.md). ## Properties ### bootstrap? > `optional` **bootstrap?**: `string`\[] Defined in: [index.ts:34](https://github.com/mertdogar/super-line/blob/130bd9adcf4898b57c097e3121e358d596527fa2/packages/adapter-libp2p/src/index.ts#L34) Bootstrap seed multiaddrs (incl. `/p2p/`) the built-in node dials on startup. *** ### identity? > `optional` **identity?**: `PrivateKey` | { `path`: `string`; } Defined in: [index.ts:42](https://github.com/mertdogar/super-line/blob/130bd9adcf4898b57c097e3121e358d596527fa2/packages/adapter-libp2p/src/index.ts#L42) Peer identity for the built-in node: a raw `PrivateKey`, or `{ path }` to load-or-create a persistent Ed25519 key on disk (stable peer ID across restarts). Omit for an ephemeral key — convenient for dev, but a startup warning fires since bootstrap lists break on restart. *** ### listen? > `optional` **listen?**: `string`\[] Defined in: [index.ts:32](https://github.com/mertdogar/super-line/blob/130bd9adcf4898b57c097e3121e358d596527fa2/packages/adapter-libp2p/src/index.ts#L32) Listen multiaddrs for the built-in node. Defaults to `['/ip4/0.0.0.0/tcp/0']` (or `/ws` for the WebSocket transport). Seed nodes should use a FIXED port so their multiaddr stays valid in others' bootstrap lists. *** ### node? > `optional` **node?**: [`PubSubLibp2p`](../type-aliases/PubSubLibp2p.md) Defined in: [index.ts:26](https://github.com/mertdogar/super-line/blob/130bd9adcf4898b57c097e3121e358d596527fa2/packages/adapter-libp2p/src/index.ts#L26) Bring your own (started) libp2p node. It must expose a gossipsub `pubsub` service (the adapter does its own loopback, so `emitSelf` can be left off). When provided, the adapter does NOT manage the node's lifecycle — `close()` leaves it running. *** ### presence? > `optional` **presence?**: `false` | { `livenessTtlMs?`: `number`; `snapshotIntervalMs?`: `number`; } Defined in: [index.ts:49](https://github.com/mertdogar/super-line/blob/130bd9adcf4898b57c097e3121e358d596527fa2/packages/adapter-libp2p/src/index.ts#L49) Cluster presence directory (powers `srv.cluster.*` / `srv.isOnline`). On by default; set `false` to disable (cluster queries then throw). Pass an object to tune timings. *** ### topic? > `optional` **topic?**: `string` Defined in: [index.ts:44](https://github.com/mertdogar/super-line/blob/130bd9adcf4898b57c097e3121e358d596527fa2/packages/adapter-libp2p/src/index.ts#L44) The single shared gossipsub topic every node joins. Defaults to `'super-line/v1'`. *** ### transport? > `optional` **transport?**: `"tcp"` | `"ws"` Defined in: [index.ts:36](https://github.com/mertdogar/super-line/blob/130bd9adcf4898b57c097e3121e358d596527fa2/packages/adapter-libp2p/src/index.ts#L36) Transport for the built-in node. Defaults to `'tcp'`. --- --- url: >- https://super-line.dogar.biz/reference/@super-line/adapter-libp2p/type-aliases/PubSubLibp2p.md --- [Documentation](../../../index.md) / [@super-line/adapter-libp2p](../index.md) / PubSubLibp2p # Type Alias: PubSubLibp2p > **PubSubLibp2p** = `Libp2p`<{ `pubsub`: `GossipSub`; }> Defined in: [index.ts:16](https://github.com/mertdogar/super-line/blob/130bd9adcf4898b57c097e3121e358d596527fa2/packages/adapter-libp2p/src/index.ts#L16) A libp2p node whose services expose a gossipsub `pubsub`. --- --- url: >- https://super-line.dogar.biz/reference/@super-line/adapter-libp2p/functions/createLibp2pAdapter.md --- [Documentation](../../../index.md) / [@super-line/adapter-libp2p](../index.md) / createLibp2pAdapter # Function: createLibp2pAdapter() > **createLibp2pAdapter**(`options?`): `Promise`<[`Adapter`](../../core/interfaces/Adapter.md) & `object`> Defined in: [index.ts:134](https://github.com/mertdogar/super-line/blob/130bd9adcf4898b57c097e3121e358d596527fa2/packages/adapter-libp2p/src/index.ts#L134) Create a libp2p (gossipsub) [Adapter](../../core/interfaces/Adapter.md) for decentralized, broker-less multi-node fan-out. All channels ride one shared gossipsub topic; each node filters incoming messages by its local subscriptions, so `subscribe` / `unsubscribe` are local bookkeeping with no network round-trip. At-most-once delivery, matching the library's model. ## Parameters ### options? [`Libp2pAdapterOptions`](../interfaces/Libp2pAdapterOptions.md) = `{}` [Libp2pAdapterOptions](../interfaces/Libp2pAdapterOptions.md) (bring your own node, or let the adapter build one from `listen` / `bootstrap`). ## Returns `Promise`<[`Adapter`](../../core/interfaces/Adapter.md) & `object`> ## Example ```ts const adapter = await createLibp2pAdapter({ bootstrap: ['/ip4/10.0.0.1/tcp/9001/p2p/12D3Koo...'] }) createSuperLineServer(api, { server, adapter }) ``` --- --- url: 'https://super-line.dogar.biz/reference/@super-line/adapter-rabbitmq.md' --- [Documentation](../../index.md) / @super-line/adapter-rabbitmq # @super-line/adapter-rabbitmq RabbitMQ adapter for [**super-line**](https://mertdogar.github.io/super-line/) — fan out rooms, topics, and the cluster event bus (`server.publish` / `server.subscribe`) across multiple server processes, with the broker doing selective per-channel routing. ```bash pnpm add @super-line/adapter-rabbitmq ``` ```ts import { createSuperLineServer } from '@super-line/server' import { webSocketServerTransport } from '@super-line/transport-websocket' import { createRabbitmqAdapter } from '@super-line/adapter-rabbitmq' import { api } from './contract' const srv = createSuperLineServer(api, { transports: [webSocketServerTransport({ server })], authenticate, adapter: await createRabbitmqAdapter('amqp://localhost:5672'), }) ``` Point every server process at the same RabbitMQ broker. Without an adapter, a single node uses the built-in in-memory adapter — add this only when you scale out. Channels become routing keys on one durable `direct` exchange; each node owns one exclusive, auto-delete queue and binds only the channels it has local members for. At-most-once delivery; built on `rabbitmq-client` (automatic reconnection + topology recovery). * 📖 Docs: * 📚 Guide: [scaling & adapters](https://mertdogar.github.io/super-line/guide/scaling-adapters) * 🧩 Source: MIT © Mert ## Interfaces * [RabbitmqAdapterOptions](interfaces/RabbitmqAdapterOptions.md) ## Functions * [createRabbitmqAdapter](functions/createRabbitmqAdapter.md) --- --- url: >- https://super-line.dogar.biz/reference/@super-line/adapter-rabbitmq/interfaces/RabbitmqAdapterOptions.md --- [Documentation](../../../index.md) / [@super-line/adapter-rabbitmq](../index.md) / RabbitmqAdapterOptions # Interface: RabbitmqAdapterOptions Defined in: [index.ts:8](https://github.com/mertdogar/super-line/blob/130bd9adcf4898b57c097e3121e358d596527fa2/packages/adapter-rabbitmq/src/index.ts#L8) Options for [createRabbitmqAdapter](../functions/createRabbitmqAdapter.md). ## Properties ### connection? > `optional` **connection?**: `Connection` Defined in: [index.ts:16](https://github.com/mertdogar/super-line/blob/130bd9adcf4898b57c097e3121e358d596527fa2/packages/adapter-rabbitmq/src/index.ts#L16) Bring your own (constructed) `rabbitmq-client` Connection — the advanced case (TLS, multi-host failover, custom heartbeat, vhost). When provided, the adapter does NOT own its lifecycle: `close()` leaves it open. *** ### exchange? > `optional` **exchange?**: `string` Defined in: [index.ts:18](https://github.com/mertdogar/super-line/blob/130bd9adcf4898b57c097e3121e358d596527fa2/packages/adapter-rabbitmq/src/index.ts#L18) The single durable `direct` exchange every node shares. Defaults to `'super-line'`. *** ### presence? > `optional` **presence?**: `false` | { `livenessTtlMs?`: `number`; `snapshotIntervalMs?`: `number`; } Defined in: [index.ts:26](https://github.com/mertdogar/super-line/blob/130bd9adcf4898b57c097e3121e358d596527fa2/packages/adapter-rabbitmq/src/index.ts#L26) Cluster presence directory (powers `srv.cluster.*` / `srv.isOnline`). On by default; set `false` to disable (cluster queries then throw). Pass an object to tune the gossip timings (defaults: `snapshotIntervalMs` 10\_000, `livenessTtlMs` 30\_000). *** ### queuePrefix? > `optional` **queuePrefix?**: `string` Defined in: [index.ts:20](https://github.com/mertdogar/super-line/blob/130bd9adcf4898b57c097e3121e358d596527fa2/packages/adapter-rabbitmq/src/index.ts#L20) Prefix for this node's exclusive auto-delete queue (`.`). Defaults to `'sl.node'`. *** ### url? > `optional` **url?**: `string` Defined in: [index.ts:10](https://github.com/mertdogar/super-line/blob/130bd9adcf4898b57c097e3121e358d596527fa2/packages/adapter-rabbitmq/src/index.ts#L10) `amqp://` (or `amqps://`) connection URL. The simple case. --- --- url: >- https://super-line.dogar.biz/reference/@super-line/adapter-rabbitmq/functions/createRabbitmqAdapter.md --- [Documentation](../../../index.md) / [@super-line/adapter-rabbitmq](../index.md) / createRabbitmqAdapter # Function: createRabbitmqAdapter() > **createRabbitmqAdapter**(`options?`): `Promise`<[`Adapter`](../../core/interfaces/Adapter.md) & `object`> Defined in: [index.ts:59](https://github.com/mertdogar/super-line/blob/130bd9adcf4898b57c097e3121e358d596527fa2/packages/adapter-rabbitmq/src/index.ts#L59) Create a RabbitMQ [Adapter](../../core/interfaces/Adapter.md) for broker-routed multi-node fan-out. Channels become routing keys on one durable `direct` exchange; each node owns one exclusive, auto-delete queue and binds only the channels it has local members for, so the broker selectively routes. At-most-once delivery, matching the library's model. Built on `rabbitmq-client` (automatic reconnection + topology recovery). ## Parameters ### options? `string` | [`RabbitmqAdapterOptions`](../interfaces/RabbitmqAdapterOptions.md) an `amqp://` URL string or [RabbitmqAdapterOptions](../interfaces/RabbitmqAdapterOptions.md). ## Returns `Promise`<[`Adapter`](../../core/interfaces/Adapter.md) & `object`> ## Example ```ts const adapter = await createRabbitmqAdapter('amqp://localhost:5672') createSuperLineServer(api, { server, adapter }) ``` --- --- url: 'https://super-line.dogar.biz/reference/@super-line/adapter-redis.md' --- [Documentation](../../index.md) / @super-line/adapter-redis # @super-line/adapter-redis Redis Pub/Sub adapter for [**super-line**](https://mertdogar.github.io/super-line/) — fan out rooms, topics, and the cluster event bus (`server.publish` / `server.subscribe`) across multiple server processes. ```bash pnpm add @super-line/adapter-redis ``` ```ts import { createSuperLineServer } from '@super-line/server' import { webSocketServerTransport } from '@super-line/transport-websocket' import { createRedisAdapter } from '@super-line/adapter-redis' import { api } from './contract' const srv = createSuperLineServer(api, { transports: [webSocketServerTransport({ server })], authenticate, adapter: createRedisAdapter('redis://localhost:6379'), }) ``` Point every server process at the same Redis. Without an adapter, a single node uses the built-in in-memory adapter — add this only when you scale out. At-most-once delivery; uses two connections (a subscriber connection can't run other commands). * 📖 Docs: * 📚 Guide: [scaling & adapters](https://mertdogar.github.io/super-line/guide/scaling-adapters) * 🧩 Source: MIT © Mert ## Interfaces * [RedisAdapterOptions](interfaces/RedisAdapterOptions.md) ## Functions * [createRedisAdapter](functions/createRedisAdapter.md) --- --- url: >- https://super-line.dogar.biz/reference/@super-line/adapter-redis/interfaces/RedisAdapterOptions.md --- [Documentation](../../../index.md) / [@super-line/adapter-redis](../index.md) / RedisAdapterOptions # Interface: RedisAdapterOptions Defined in: [index.ts:5](https://github.com/mertdogar/super-line/blob/130bd9adcf4898b57c097e3121e358d596527fa2/packages/adapter-redis/src/index.ts#L5) Options for [createRedisAdapter](../functions/createRedisAdapter.md). ## Properties ### presenceTtlMs? > `optional` **presenceTtlMs?**: `number` Defined in: [index.ts:13](https://github.com/mertdogar/super-line/blob/130bd9adcf4898b57c097e3121e358d596527fa2/packages/adapter-redis/src/index.ts#L13) How long a node's liveness key lives between heartbeats (ms). A node whose key expires has its connections excluded from cluster queries. Must exceed the server's heartbeat interval. Defaults to `90_000`. *** ### url? > `optional` **url?**: `string` Defined in: [index.ts:7](https://github.com/mertdogar/super-line/blob/130bd9adcf4898b57c097e3121e358d596527fa2/packages/adapter-redis/src/index.ts#L7) `redis://` connection URL (defaults to ioredis's default localhost:6379). --- --- url: >- https://super-line.dogar.biz/reference/@super-line/adapter-redis/functions/createRedisAdapter.md --- [Documentation](../../../index.md) / [@super-line/adapter-redis](../index.md) / createRedisAdapter # Function: createRedisAdapter() > **createRedisAdapter**(`options?`): [`Adapter`](../../core/interfaces/Adapter.md) Defined in: [index.ts:138](https://github.com/mertdogar/super-line/blob/130bd9adcf4898b57c097e3121e358d596527fa2/packages/adapter-redis/src/index.ts#L138) Create a Redis Pub/Sub [Adapter](../../core/interfaces/Adapter.md) for multi-node fan-out. Pass the same URL to every server process so rooms, topics, and the cluster event bus reach clients and server subscribers on any node. Uses two connections (a subscriber connection can't run other commands); at-most-once delivery, matching the library's model. ## Parameters ### options? `string` | [`RedisAdapterOptions`](../interfaces/RedisAdapterOptions.md) a `redis://` URL string or [RedisAdapterOptions](../interfaces/RedisAdapterOptions.md). ## Returns [`Adapter`](../../core/interfaces/Adapter.md) ## Example ```ts createSuperLineServer(api, { server, adapter: createRedisAdapter('redis://localhost:6379') }) ``` --- --- url: 'https://super-line.dogar.biz/reference/@super-line/adapter-zeromq.md' --- [Documentation](../../index.md) / @super-line/adapter-zeromq # @super-line/adapter-zeromq ZeroMQ adapter for [**super-line**](https://mertdogar.github.io/super-line/) — fan out rooms, topics, and the cluster event bus (`server.publish` / `server.subscribe`) across multiple server processes, broker-free. ```bash pnpm add @super-line/adapter-zeromq ``` ```ts import { createSuperLineServer } from '@super-line/server' import { webSocketServerTransport } from '@super-line/transport-websocket' import { createZeroMqAdapter } from '@super-line/adapter-zeromq' import { api } from './contract' const srv = createSuperLineServer(api, { transports: [webSocketServerTransport({ server })], authenticate, adapter: await createZeroMqAdapter({ bind: 'tcp://0.0.0.0:5555', peers: ['tcp://node-b:5555', 'tcp://node-c:5555'], }), }) ``` Each node binds a PUB socket and connects to its peers — a brokerless mesh, no central server to run. Without an adapter, a single node uses the built-in in-memory adapter — add this only when you scale out. At-most-once delivery; `zeromq` is a native addon. For a large fan-out you can run the bundled forwarder proxy instead of a full mesh: ```bash super-line-zeromq-proxy --xsub tcp://0.0.0.0:5555 --xpub tcp://0.0.0.0:5556 ``` * 📖 Docs: * 📚 Guide: [scaling & adapters](https://mertdogar.github.io/super-line/guide/scaling-adapters) * 🧩 Source: MIT © Mert ## Interfaces * [ZeroMqByoOptions](interfaces/ZeroMqByoOptions.md) * [ZeroMqMeshOptions](interfaces/ZeroMqMeshOptions.md) * [ZeroMqProxy](interfaces/ZeroMqProxy.md) * [ZeroMqProxyModeOptions](interfaces/ZeroMqProxyModeOptions.md) * [ZeroMqProxyOptions](interfaces/ZeroMqProxyOptions.md) ## Type Aliases * [ZeroMqAdapter](type-aliases/ZeroMqAdapter.md) * [ZeroMqAdapterOptions](type-aliases/ZeroMqAdapterOptions.md) * [ZeroMqPresenceOption](type-aliases/ZeroMqPresenceOption.md) ## Functions * [createZeroMqAdapter](functions/createZeroMqAdapter.md) * [createZeroMqProxy](functions/createZeroMqProxy.md) --- --- url: >- https://super-line.dogar.biz/reference/@super-line/adapter-zeromq/interfaces/ZeroMqByoOptions.md --- [Documentation](../../../index.md) / [@super-line/adapter-zeromq](../index.md) / ZeroMqByoOptions # Interface: ZeroMqByoOptions Defined in: [index.ts:39](https://github.com/mertdogar/super-line/blob/130bd9adcf4898b57c097e3121e358d596527fa2/packages/adapter-zeromq/src/index.ts#L39) Bring your own pre-wired sockets — the adapter uses them as-is and does NOT own their lifecycle. ## Properties ### presence? > `optional` **presence?**: [`ZeroMqPresenceOption`](../type-aliases/ZeroMqPresenceOption.md) Defined in: [index.ts:45](https://github.com/mertdogar/super-line/blob/130bd9adcf4898b57c097e3121e358d596527fa2/packages/adapter-zeromq/src/index.ts#L45) Cluster presence directory. On by default. *** ### pub > **pub**: `Publisher` Defined in: [index.ts:41](https://github.com/mertdogar/super-line/blob/130bd9adcf4898b57c097e3121e358d596527fa2/packages/adapter-zeromq/src/index.ts#L41) A PUB socket you've already bound/connected. *** ### sub > **sub**: `Subscriber` Defined in: [index.ts:43](https://github.com/mertdogar/super-line/blob/130bd9adcf4898b57c097e3121e358d596527fa2/packages/adapter-zeromq/src/index.ts#L43) A SUB socket you've already connected (the adapter calls `subscribe`/`unsubscribe` on it). --- --- url: >- https://super-line.dogar.biz/reference/@super-line/adapter-zeromq/interfaces/ZeroMqMeshOptions.md --- [Documentation](../../../index.md) / [@super-line/adapter-zeromq](../index.md) / ZeroMqMeshOptions # Interface: ZeroMqMeshOptions Defined in: [index.ts:13](https://github.com/mertdogar/super-line/blob/130bd9adcf4898b57c097e3121e358d596527fa2/packages/adapter-zeromq/src/index.ts#L13) Brokerless full-mesh: this node binds a PUB and connects a SUB to every peer's PUB. ## Properties ### bind > **bind**: `string` Defined in: [index.ts:16](https://github.com/mertdogar/super-line/blob/130bd9adcf4898b57c097e3121e358d596527fa2/packages/adapter-zeromq/src/index.ts#L16) This node's own PUB endpoint to bind (e.g. `tcp://0.0.0.0:5555`, or `tcp://127.0.0.1:0` for an OS-picked port). *** ### mode? > `optional` **mode?**: `"mesh"` Defined in: [index.ts:14](https://github.com/mertdogar/super-line/blob/130bd9adcf4898b57c097e3121e358d596527fa2/packages/adapter-zeromq/src/index.ts#L14) *** ### peers? > `optional` **peers?**: `string`\[] Defined in: [index.ts:18](https://github.com/mertdogar/super-line/blob/130bd9adcf4898b57c097e3121e358d596527fa2/packages/adapter-zeromq/src/index.ts#L18) Other nodes' PUB endpoints to connect a SUB to. Lazy + auto-reconnecting, so peers may start later. *** ### presence? > `optional` **presence?**: [`ZeroMqPresenceOption`](../type-aliases/ZeroMqPresenceOption.md) Defined in: [index.ts:22](https://github.com/mertdogar/super-line/blob/130bd9adcf4898b57c097e3121e358d596527fa2/packages/adapter-zeromq/src/index.ts#L22) Cluster presence directory. On by default. *** ### sendHighWaterMark? > `optional` **sendHighWaterMark?**: `number` Defined in: [index.ts:20](https://github.com/mertdogar/super-line/blob/130bd9adcf4898b57c097e3121e358d596527fa2/packages/adapter-zeromq/src/index.ts#L20) PUB/SUB high-water-mark (messages buffered per peer before silent drops). Defaults to `100_000`. --- --- url: >- https://super-line.dogar.biz/reference/@super-line/adapter-zeromq/interfaces/ZeroMqProxy.md --- [Documentation](../../../index.md) / [@super-line/adapter-zeromq](../index.md) / ZeroMqProxy # Interface: ZeroMqProxy Defined in: [index.ts:193](https://github.com/mertdogar/super-line/blob/130bd9adcf4898b57c097e3121e358d596527fa2/packages/adapter-zeromq/src/index.ts#L193) A running forwarder; `stop()` terminates it and unbinds both endpoints. ## Properties ### backendUrl > **backendUrl**: `string` Defined in: [index.ts:197](https://github.com/mertdogar/super-line/blob/130bd9adcf4898b57c097e3121e358d596527fa2/packages/adapter-zeromq/src/index.ts#L197) The resolved backend (XPUB) endpoint node SUBs connect to. *** ### frontendUrl > **frontendUrl**: `string` Defined in: [index.ts:195](https://github.com/mertdogar/super-line/blob/130bd9adcf4898b57c097e3121e358d596527fa2/packages/adapter-zeromq/src/index.ts#L195) The resolved frontend (XSUB) endpoint node PUBs connect to. ## Methods ### stop() > **stop**(): `Promise`<`void`> Defined in: [index.ts:199](https://github.com/mertdogar/super-line/blob/130bd9adcf4898b57c097e3121e358d596527fa2/packages/adapter-zeromq/src/index.ts#L199) Terminate the proxy and release its sockets. #### Returns `Promise`<`void`> --- --- url: >- https://super-line.dogar.biz/reference/@super-line/adapter-zeromq/interfaces/ZeroMqProxyModeOptions.md --- [Documentation](../../../index.md) / [@super-line/adapter-zeromq](../index.md) / ZeroMqProxyModeOptions # Interface: ZeroMqProxyModeOptions Defined in: [index.ts:26](https://github.com/mertdogar/super-line/blob/130bd9adcf4898b57c097e3121e358d596527fa2/packages/adapter-zeromq/src/index.ts#L26) Central forwarder: this node connects its PUB to the proxy frontend and its SUB to the backend. ## Properties ### backendUrl > **backendUrl**: `string` Defined in: [index.ts:31](https://github.com/mertdogar/super-line/blob/130bd9adcf4898b57c097e3121e358d596527fa2/packages/adapter-zeromq/src/index.ts#L31) The proxy's backend (XPUB) endpoint — this node's SUB connects here. *** ### frontendUrl > **frontendUrl**: `string` Defined in: [index.ts:29](https://github.com/mertdogar/super-line/blob/130bd9adcf4898b57c097e3121e358d596527fa2/packages/adapter-zeromq/src/index.ts#L29) The proxy's frontend (XSUB) endpoint — this node's PUB connects here. *** ### mode > **mode**: `"proxy"` Defined in: [index.ts:27](https://github.com/mertdogar/super-line/blob/130bd9adcf4898b57c097e3121e358d596527fa2/packages/adapter-zeromq/src/index.ts#L27) *** ### presence? > `optional` **presence?**: [`ZeroMqPresenceOption`](../type-aliases/ZeroMqPresenceOption.md) Defined in: [index.ts:35](https://github.com/mertdogar/super-line/blob/130bd9adcf4898b57c097e3121e358d596527fa2/packages/adapter-zeromq/src/index.ts#L35) Cluster presence directory. On by default. *** ### sendHighWaterMark? > `optional` **sendHighWaterMark?**: `number` Defined in: [index.ts:33](https://github.com/mertdogar/super-line/blob/130bd9adcf4898b57c097e3121e358d596527fa2/packages/adapter-zeromq/src/index.ts#L33) PUB/SUB high-water-mark. Defaults to `100_000`. --- --- url: >- https://super-line.dogar.biz/reference/@super-line/adapter-zeromq/interfaces/ZeroMqProxyOptions.md --- [Documentation](../../../index.md) / [@super-line/adapter-zeromq](../index.md) / ZeroMqProxyOptions # Interface: ZeroMqProxyOptions Defined in: [index.ts:183](https://github.com/mertdogar/super-line/blob/130bd9adcf4898b57c097e3121e358d596527fa2/packages/adapter-zeromq/src/index.ts#L183) Options for [createZeroMqProxy](../functions/createZeroMqProxy.md). ## Properties ### backendUrl > **backendUrl**: `string` Defined in: [index.ts:187](https://github.com/mertdogar/super-line/blob/130bd9adcf4898b57c097e3121e358d596527fa2/packages/adapter-zeromq/src/index.ts#L187) Backend (XPUB) endpoint to bind — node SUBs connect here to receive. *** ### frontendUrl > **frontendUrl**: `string` Defined in: [index.ts:185](https://github.com/mertdogar/super-line/blob/130bd9adcf4898b57c097e3121e358d596527fa2/packages/adapter-zeromq/src/index.ts#L185) Frontend (XSUB) endpoint to bind — node PUBs connect here to publish in. *** ### sendHighWaterMark? > `optional` **sendHighWaterMark?**: `number` Defined in: [index.ts:189](https://github.com/mertdogar/super-line/blob/130bd9adcf4898b57c097e3121e358d596527fa2/packages/adapter-zeromq/src/index.ts#L189) High-water-mark for both legs. Defaults to `100_000`. --- --- url: >- https://super-line.dogar.biz/reference/@super-line/adapter-zeromq/type-aliases/ZeroMqAdapter.md --- [Documentation](../../../index.md) / [@super-line/adapter-zeromq](../index.md) / ZeroMqAdapter # Type Alias: ZeroMqAdapter > **ZeroMqAdapter** = [`Adapter`](../../core/interfaces/Adapter.md) & `object` Defined in: [index.ts:52](https://github.com/mertdogar/super-line/blob/130bd9adcf4898b57c097e3121e358d596527fa2/packages/adapter-zeromq/src/index.ts#L52) A mesh adapter also exposes its resolved bind endpoint, so a node bound to `:0` can advertise it to peers. ## Type Declaration ### endpoint > **endpoint**: `string` --- --- url: >- https://super-line.dogar.biz/reference/@super-line/adapter-zeromq/type-aliases/ZeroMqAdapterOptions.md --- [Documentation](../../../index.md) / [@super-line/adapter-zeromq](../index.md) / ZeroMqAdapterOptions # Type Alias: ZeroMqAdapterOptions > **ZeroMqAdapterOptions** = [`ZeroMqMeshOptions`](../interfaces/ZeroMqMeshOptions.md) | [`ZeroMqProxyModeOptions`](../interfaces/ZeroMqProxyModeOptions.md) | [`ZeroMqByoOptions`](../interfaces/ZeroMqByoOptions.md) Defined in: [index.ts:49](https://github.com/mertdogar/super-line/blob/130bd9adcf4898b57c097e3121e358d596527fa2/packages/adapter-zeromq/src/index.ts#L49) Options for [createZeroMqAdapter](../functions/createZeroMqAdapter.md). --- --- url: >- https://super-line.dogar.biz/reference/@super-line/adapter-zeromq/type-aliases/ZeroMqPresenceOption.md --- [Documentation](../../../index.md) / [@super-line/adapter-zeromq](../index.md) / ZeroMqPresenceOption # Type Alias: ZeroMqPresenceOption > **ZeroMqPresenceOption** = `false` | { `livenessTtlMs?`: `number`; `snapshotIntervalMs?`: `number`; } Defined in: [index.ts:10](https://github.com/mertdogar/super-line/blob/130bd9adcf4898b57c097e3121e358d596527fa2/packages/adapter-zeromq/src/index.ts#L10) Cluster presence directory (powers `srv.cluster.*` / `srv.isOnline`). On by default; set `false` to disable (cluster queries then throw). Pass an object to tune timings. --- --- url: >- https://super-line.dogar.biz/reference/@super-line/adapter-zeromq/functions/createZeroMqAdapter.md --- [Documentation](../../../index.md) / [@super-line/adapter-zeromq](../index.md) / createZeroMqAdapter # Function: createZeroMqAdapter() ## Call Signature > **createZeroMqAdapter**(`options`): `Promise`<[`ZeroMqAdapter`](../type-aliases/ZeroMqAdapter.md)> Defined in: [index.ts:159](https://github.com/mertdogar/super-line/blob/130bd9adcf4898b57c097e3121e358d596527fa2/packages/adapter-zeromq/src/index.ts#L159) Create a ZeroMQ [Adapter](../../core/interfaces/Adapter.md) for multi-node fan-out. Three shapes: * **mesh** (default): brokerless full mesh — this node binds a PUB and connects a SUB to every peer. Returns the resolved bind `endpoint`. * **proxy**: connect through a central [createZeroMqProxy](createZeroMqProxy.md) forwarder. * **BYO**: hand in your own pre-wired `{ pub, sub }` sockets (the adapter does not own their lifecycle — `close()` leaves them open). ZeroMQ's lazy connect + auto-reconnect mean peers may start in any order. At-most-once delivery, matching the library's model. ### Parameters #### options [`ZeroMqMeshOptions`](../interfaces/ZeroMqMeshOptions.md) ### Returns `Promise`<[`ZeroMqAdapter`](../type-aliases/ZeroMqAdapter.md)> ### Example ```ts const adapter = await createZeroMqAdapter({ bind: 'tcp://0.0.0.0:5555', peers: ['tcp://node-b:5555'] }) createSuperLineServer(api, { server, adapter }) ``` ## Call Signature > **createZeroMqAdapter**(`options`): `Promise`<[`Adapter`](../../core/interfaces/Adapter.md)> Defined in: [index.ts:160](https://github.com/mertdogar/super-line/blob/130bd9adcf4898b57c097e3121e358d596527fa2/packages/adapter-zeromq/src/index.ts#L160) Create a ZeroMQ [Adapter](../../core/interfaces/Adapter.md) for multi-node fan-out. Three shapes: * **mesh** (default): brokerless full mesh — this node binds a PUB and connects a SUB to every peer. Returns the resolved bind `endpoint`. * **proxy**: connect through a central [createZeroMqProxy](createZeroMqProxy.md) forwarder. * **BYO**: hand in your own pre-wired `{ pub, sub }` sockets (the adapter does not own their lifecycle — `close()` leaves them open). ZeroMQ's lazy connect + auto-reconnect mean peers may start in any order. At-most-once delivery, matching the library's model. ### Parameters #### options [`ZeroMqProxyModeOptions`](../interfaces/ZeroMqProxyModeOptions.md) | [`ZeroMqByoOptions`](../interfaces/ZeroMqByoOptions.md) ### Returns `Promise`<[`Adapter`](../../core/interfaces/Adapter.md)> ### Example ```ts const adapter = await createZeroMqAdapter({ bind: 'tcp://0.0.0.0:5555', peers: ['tcp://node-b:5555'] }) createSuperLineServer(api, { server, adapter }) ``` --- --- url: >- https://super-line.dogar.biz/reference/@super-line/adapter-zeromq/functions/createZeroMqProxy.md --- [Documentation](../../../index.md) / [@super-line/adapter-zeromq](../index.md) / createZeroMqProxy # Function: createZeroMqProxy() > **createZeroMqProxy**(`options`): `Promise`<[`ZeroMqProxy`](../interfaces/ZeroMqProxy.md)> Defined in: [index.ts:213](https://github.com/mertdogar/super-line/blob/130bd9adcf4898b57c097e3121e358d596527fa2/packages/adapter-zeromq/src/index.ts#L213) Run a ZeroMQ XSUB⇄XPUB forwarder — the central broker for `mode: 'proxy'` adapters. Nodes connect their PUB to `frontendUrl` and their SUB to `backendUrl`; the proxy relays every message (and propagates subscriptions back to publishers, so native channel filtering still works through it). ## Parameters ### options [`ZeroMqProxyOptions`](../interfaces/ZeroMqProxyOptions.md) ## Returns `Promise`<[`ZeroMqProxy`](../interfaces/ZeroMqProxy.md)> ## Example ```ts const proxy = await createZeroMqProxy({ frontendUrl: 'tcp://0.0.0.0:5557', backendUrl: 'tcp://0.0.0.0:5558' }) ``` --- --- url: 'https://super-line.dogar.biz/reference/@super-line/client.md' --- [Documentation](../../index.md) / @super-line/client # @super-line/client The client for [**super-line**](https://mertdogar.github.io/super-line/) — end-to-end typesafe WebSockets for TypeScript. A typed proxy over the global `WebSocket`: call requests, listen to events, subscribe to topics — with auto-reconnect and at-most-once delivery. ```bash pnpm add @super-line/core @super-line/client @super-line/transport-websocket zod ``` ```ts import { createSuperLineClient } from '@super-line/client' import { webSocketClientTransport } from '@super-line/transport-websocket' import { api } from './contract' const client = createSuperLineClient(api, { transport: webSocketClientTransport({ url: 'ws://localhost:3000' }), role: 'user', }) client.on('message', (m) => console.log(m.text)) // typed const sub = client.subscribe('prices', (p) => render(p)) await sub.ready const out = await client.send({ text: 'hi' }) // throws typed SuperLineError on failure client.close() ``` The client is narrowed to its `role`'s surface (`shared ∪ role`). The wire is carried by a pluggable transport — [`@super-line/transport-websocket`](https://www.npmjs.com/package/@super-line/transport-websocket) provides the WS transport shown above; other transports (HTTP/SSE, libp2p) are available — see the Transports guide. Works in browsers and Node 22+ (pass `webSocketClientTransport({ url, WebSocket })` on older runtimes). * 📖 Docs: * 📚 Guides: [requests](https://mertdogar.github.io/super-line/guide/requests), [reconnection & delivery](https://mertdogar.github.io/super-line/guide/reconnection-delivery) * 📕 API reference: * 🧩 Source: MIT © Mert ## Interfaces * [BackoffOptions](interfaces/BackoffOptions.md) * [CallOptions](interfaces/CallOptions.md) * [ClientStoreHandle](interfaces/ClientStoreHandle.md) * [ResourceHandle](interfaces/ResourceHandle.md) * [Subscription](interfaces/Subscription.md) * [SuperLineClientOptions](interfaces/SuperLineClientOptions.md) * [ValidationErrorInfo](interfaces/ValidationErrorInfo.md) ## Type Aliases * [ClientMethods](type-aliases/ClientMethods.md) * [ServerHandlers](type-aliases/ServerHandlers.md) * [SuperLineClient](type-aliases/SuperLineClient.md) ## Functions * [backoffDelay](functions/backoffDelay.md) * [createSuperLineClient](functions/createSuperLineClient.md) --- --- url: >- https://super-line.dogar.biz/reference/@super-line/client/interfaces/BackoffOptions.md --- [Documentation](../../../index.md) / [@super-line/client](../index.md) / BackoffOptions # Interface: BackoffOptions Defined in: [backoff.ts:2](https://github.com/mertdogar/super-line/blob/130bd9adcf4898b57c097e3121e358d596527fa2/packages/client/src/backoff.ts#L2) Tuning for [backoffDelay](../functions/backoffDelay.md). ## Properties ### baseMs > **baseMs**: `number` Defined in: [backoff.ts:4](https://github.com/mertdogar/super-line/blob/130bd9adcf4898b57c097e3121e358d596527fa2/packages/client/src/backoff.ts#L4) Initial delay in ms (attempt 0). *** ### factor > **factor**: `number` Defined in: [backoff.ts:8](https://github.com/mertdogar/super-line/blob/130bd9adcf4898b57c097e3121e358d596527fa2/packages/client/src/backoff.ts#L8) Exponential growth factor per attempt. *** ### maxMs > **maxMs**: `number` Defined in: [backoff.ts:6](https://github.com/mertdogar/super-line/blob/130bd9adcf4898b57c097e3121e358d596527fa2/packages/client/src/backoff.ts#L6) Upper bound on the delay in ms. --- --- url: >- https://super-line.dogar.biz/reference/@super-line/client/interfaces/CallOptions.md --- [Documentation](../../../index.md) / [@super-line/client](../index.md) / CallOptions # Interface: CallOptions Defined in: [index.ts:33](https://github.com/mertdogar/super-line/blob/130bd9adcf4898b57c097e3121e358d596527fa2/packages/client/src/index.ts#L33) Per-call options for a request. ## Properties ### signal? > `optional` **signal?**: `AbortSignal` Defined in: [index.ts:37](https://github.com/mertdogar/super-line/blob/130bd9adcf4898b57c097e3121e358d596527fa2/packages/client/src/index.ts#L37) Abort the request; rejects with a `BAD_REQUEST` `SuperLineError`. *** ### timeoutMs? > `optional` **timeoutMs?**: `number` Defined in: [index.ts:35](https://github.com/mertdogar/super-line/blob/130bd9adcf4898b57c097e3121e358d596527fa2/packages/client/src/index.ts#L35) Override the default request timeout (ms). `0` disables the timeout. --- --- url: >- https://super-line.dogar.biz/reference/@super-line/client/interfaces/ClientStoreHandle.md --- [Documentation](../../../index.md) / [@super-line/client](../index.md) / ClientStoreHandle # Interface: ClientStoreHandle Defined in: [index.ts:113](https://github.com/mertdogar/super-line/blob/130bd9adcf4898b57c097e3121e358d596527fa2/packages/client/src/index.ts#L113) Client-side handle for one configured Store, reached via `client.store(name)`. ## Methods ### open() > **open**(`id`): [`ResourceHandle`](ResourceHandle.md) Defined in: [index.ts:115](https://github.com/mertdogar/super-line/blob/130bd9adcf4898b57c097e3121e358d596527fa2/packages/client/src/index.ts#L115) Open a reactive handle for a Resource (catch-up snapshot + live changes + write-through). #### Parameters ##### id `string` #### Returns [`ResourceHandle`](ResourceHandle.md) *** ### read() > **read**(`id`): `Promise`<`unknown`> Defined in: [index.ts:117](https://github.com/mertdogar/super-line/blob/130bd9adcf4898b57c097e3121e358d596527fa2/packages/client/src/index.ts#L117) One-shot read of a Resource's current value. #### Parameters ##### id `string` #### Returns `Promise`<`unknown`> *** ### write() > **write**(`id`, `data`): `Promise`<`void`> Defined in: [index.ts:119](https://github.com/mertdogar/super-line/blob/130bd9adcf4898b57c097e3121e358d596527fa2/packages/client/src/index.ts#L119) One-shot replace of a Resource's value (last-writer-wins). #### Parameters ##### id `string` ##### data `unknown` #### Returns `Promise`<`void`> --- --- url: >- https://super-line.dogar.biz/reference/@super-line/client/interfaces/ResourceHandle.md --- [Documentation](../../../index.md) / [@super-line/client](../index.md) / ResourceHandle # Interface: ResourceHandle Defined in: [index.ts:97](https://github.com/mertdogar/super-line/blob/130bd9adcf4898b57c097e3121e358d596527fa2/packages/client/src/index.ts#L97) A reactive handle over one opened Store Resource (mirrors super-store's `StoreValue` surface). `data` is untyped — stores are off-contract (ADR-0003). `set`/`update` mutate the local replica and write the resulting Change through to the server. ## Properties ### ready > `readonly` **ready**: `Promise`<`void`> Defined in: [index.ts:107](https://github.com/mertdogar/super-line/blob/130bd9adcf4898b57c097e3121e358d596527fa2/packages/client/src/index.ts#L107) Resolves once the catch-up snapshot has been applied; rejects if the open is denied. ## Methods ### close() > **close**(): `void` Defined in: [index.ts:109](https://github.com/mertdogar/super-line/blob/130bd9adcf4898b57c097e3121e358d596527fa2/packages/client/src/index.ts#L109) Stop receiving changes and tell the server to unsubscribe. #### Returns `void` *** ### getSnapshot() > **getSnapshot**(): `unknown` Defined in: [index.ts:99](https://github.com/mertdogar/super-line/blob/130bd9adcf4898b57c097e3121e358d596527fa2/packages/client/src/index.ts#L99) The current snapshot (`undefined` until the catch-up snapshot arrives). #### Returns `unknown` *** ### set() > **set**(`data`): `void` Defined in: [index.ts:103](https://github.com/mertdogar/super-line/blob/130bd9adcf4898b57c097e3121e358d596527fa2/packages/client/src/index.ts#L103) Replace the value (LWW) or mutate the local doc (CRDT); the Change is sent to the server. #### Parameters ##### data `unknown` #### Returns `void` *** ### subscribe() > **subscribe**(`cb`): () => `void` Defined in: [index.ts:101](https://github.com/mertdogar/super-line/blob/130bd9adcf4898b57c097e3121e358d596527fa2/packages/client/src/index.ts#L101) Subscribe to changes (local writes + remote merges). Returns an unsubscribe fn. #### Parameters ##### cb () => `void` #### Returns () => `void` *** ### update() > **update**(`partial`): `void` Defined in: [index.ts:105](https://github.com/mertdogar/super-line/blob/130bd9adcf4898b57c097e3121e358d596527fa2/packages/client/src/index.ts#L105) Merge a partial update; the Change is sent to the server. #### Parameters ##### partial `unknown` #### Returns `void` --- --- url: >- https://super-line.dogar.biz/reference/@super-line/client/interfaces/Subscription.md --- [Documentation](../../../index.md) / [@super-line/client](../index.md) / Subscription # Interface: Subscription Defined in: [index.ts:41](https://github.com/mertdogar/super-line/blob/130bd9adcf4898b57c097e3121e358d596527fa2/packages/client/src/index.ts#L41) A topic subscription handle returned by `client.subscribe`. ## Properties ### ready > `readonly` **ready**: `Promise`<`void`> Defined in: [index.ts:43](https://github.com/mertdogar/super-line/blob/130bd9adcf4898b57c097e3121e358d596527fa2/packages/client/src/index.ts#L43) Resolves when the server acknowledges the subscribe; rejects if denied or disconnected. ## Methods ### unsubscribe() > **unsubscribe**(): `void` Defined in: [index.ts:45](https://github.com/mertdogar/super-line/blob/130bd9adcf4898b57c097e3121e358d596527fa2/packages/client/src/index.ts#L45) Stop receiving the topic and tell the server to unsubscribe. #### Returns `void` --- --- url: >- https://super-line.dogar.biz/reference/@super-line/client/interfaces/SuperLineClientOptions.md --- [Documentation](../../../index.md) / [@super-line/client](../index.md) / SuperLineClientOptions # Interface: SuperLineClientOptions\ Defined in: [index.ts:131](https://github.com/mertdogar/super-line/blob/130bd9adcf4898b57c097e3121e358d596527fa2/packages/client/src/index.ts#L131) Options for [createSuperLineClient](../functions/createSuperLineClient.md). ## Type Parameters ### C `C` *extends* [`Contract`](../../core/interfaces/Contract.md) ### R `R` *extends* [`RoleOf`](../../core/type-aliases/RoleOf.md)<`C`> ## Properties ### onStoreError? > `optional` **onStoreError?**: (`error`, `info`) => `void` Defined in: [index.ts:152](https://github.com/mertdogar/super-line/blob/130bd9adcf4898b57c097e3121e358d596527fa2/packages/client/src/index.ts#L152) Called when a store write is rejected by the server (e.g. FORBIDDEN). Default: logs to console. #### Parameters ##### error `unknown` ##### info ###### id `string` ###### store `string` #### Returns `void` *** ### onValidationError? > `optional` **onValidationError?**: (`error`, `info`) => `void` Defined in: [index.ts:145](https://github.com/mertdogar/super-line/blob/130bd9adcf4898b57c097e3121e358d596527fa2/packages/client/src/index.ts#L145) Called when an inbound payload fails validation (only with `validate: 'inbound'`). #### Parameters ##### error `unknown` ##### info [`ValidationErrorInfo`](ValidationErrorInfo.md) #### Returns `void` *** ### params? > `optional` **params?**: `Record`<`string`, `string`> Defined in: [index.ts:137](https://github.com/mertdogar/super-line/blob/130bd9adcf4898b57c097e3121e358d596527fa2/packages/client/src/index.ts#L137) Extra handshake params passed to the transport (read in `authenticate`); `role` is added automatically. *** ### reconnect? > `optional` **reconnect?**: `boolean` Defined in: [index.ts:154](https://github.com/mertdogar/super-line/blob/130bd9adcf4898b57c097e3121e358d596527fa2/packages/client/src/index.ts#L154) Auto-reconnect on drop. Defaults to `true`. *** ### reconnectBaseMs? > `optional` **reconnectBaseMs?**: `number` Defined in: [index.ts:156](https://github.com/mertdogar/super-line/blob/130bd9adcf4898b57c097e3121e358d596527fa2/packages/client/src/index.ts#L156) Initial reconnect backoff in ms. Defaults to `500`. *** ### reconnectFactor? > `optional` **reconnectFactor?**: `number` Defined in: [index.ts:160](https://github.com/mertdogar/super-line/blob/130bd9adcf4898b57c097e3121e358d596527fa2/packages/client/src/index.ts#L160) Backoff growth factor. Defaults to `2`. *** ### reconnectMaxMs? > `optional` **reconnectMaxMs?**: `number` Defined in: [index.ts:158](https://github.com/mertdogar/super-line/blob/130bd9adcf4898b57c097e3121e358d596527fa2/packages/client/src/index.ts#L158) Maximum reconnect backoff in ms. Defaults to `30000`. *** ### role > **role**: `R` Defined in: [index.ts:135](https://github.com/mertdogar/super-line/blob/130bd9adcf4898b57c097e3121e358d596527fa2/packages/client/src/index.ts#L135) This client's role; narrows the surface and is sent to the server to verify. *** ### serializer? > `optional` **serializer?**: [`Serializer`](../../core/interfaces/Serializer.md) Defined in: [index.ts:139](https://github.com/mertdogar/super-line/blob/130bd9adcf4898b57c097e3121e358d596527fa2/packages/client/src/index.ts#L139) Wire serializer; MUST match the server. Defaults to `jsonSerializer`. *** ### stores? > `optional` **stores?**: `Record`<`string`, [`ClientStore`](../../core/interfaces/ClientStore.md)> Defined in: [index.ts:150](https://github.com/mertdogar/super-line/blob/130bd9adcf4898b57c097e3121e358d596527fa2/packages/client/src/index.ts#L150) Client halves of the Store pairs, keyed by name to match the server's `stores` (`{ scene: crdtStoreClient(), config: memoryStoreClient() }`). Surfaced as `client.store(name)`. *** ### timeoutMs? > `optional` **timeoutMs?**: `number` Defined in: [index.ts:141](https://github.com/mertdogar/super-line/blob/130bd9adcf4898b57c097e3121e358d596527fa2/packages/client/src/index.ts#L141) Default request timeout in ms. Defaults to `30000`. *** ### transport > **transport**: [`ClientTransport`](../../core/interfaces/ClientTransport.md) Defined in: [index.ts:133](https://github.com/mertdogar/super-line/blob/130bd9adcf4898b57c097e3121e358d596527fa2/packages/client/src/index.ts#L133) The transport to dial on, e.g. `webSocketClientTransport({ url: 'ws://localhost:3000' })`. *** ### validate? > `optional` **validate?**: `"off"` | `"inbound"` Defined in: [index.ts:143](https://github.com/mertdogar/super-line/blob/130bd9adcf4898b57c097e3121e358d596527fa2/packages/client/src/index.ts#L143) `'inbound'` re-validates server→client payloads against the contract (catches drift). Default `'off'`. --- --- url: >- https://super-line.dogar.biz/reference/@super-line/client/interfaces/ValidationErrorInfo.md --- [Documentation](../../../index.md) / [@super-line/client](../index.md) / ValidationErrorInfo # Interface: ValidationErrorInfo Defined in: [index.ts:123](https://github.com/mertdogar/super-line/blob/130bd9adcf4898b57c097e3121e358d596527fa2/packages/client/src/index.ts#L123) Describes which inbound payload failed validation, passed to `onValidationError`. ## Properties ### kind > **kind**: `"event"` | `"topic"` | `"response"` Defined in: [index.ts:125](https://github.com/mertdogar/super-line/blob/130bd9adcf4898b57c097e3121e358d596527fa2/packages/client/src/index.ts#L125) The kind of inbound payload that failed. *** ### name > **name**: `string` Defined in: [index.ts:127](https://github.com/mertdogar/super-line/blob/130bd9adcf4898b57c097e3121e358d596527fa2/packages/client/src/index.ts#L127) The request/event/topic name. --- --- url: >- https://super-line.dogar.biz/reference/@super-line/client/type-aliases/ClientMethods.md --- [Documentation](../../../index.md) / [@super-line/client](../index.md) / ClientMethods # Type Alias: ClientMethods\ > **ClientMethods**<`C`, `R`> = `{ [K in keyof Requests]: (input: ClientInput[K]>, opts?: CallOptions) => Promise[K]>> }` Defined in: [index.ts:58](https://github.com/mertdogar/super-line/blob/130bd9adcf4898b57c097e3121e358d596527fa2/packages/client/src/index.ts#L58) The request-calling half of [SuperLineClient](SuperLineClient.md) (one method per request in the role's surface). ## Type Parameters ### C `C` *extends* [`Contract`](../../core/interfaces/Contract.md) ### R `R` *extends* [`RoleOf`](../../core/type-aliases/RoleOf.md)<`C`> --- --- url: >- https://super-line.dogar.biz/reference/@super-line/client/type-aliases/ServerHandlers.md --- [Documentation](../../../index.md) / [@super-line/client](../index.md) / ServerHandlers # Type Alias: ServerHandlers\ > **ServerHandlers**<`C`, `R`> = `{ [K in keyof ServerRequests]?: (input: ServerInput[K]>) => Awaitable[K]>> }` Defined in: [index.ts:51](https://github.com/mertdogar/super-line/blob/130bd9adcf4898b57c097e3121e358d596527fa2/packages/client/src/index.ts#L51) Handlers answering server→client requests for the role's surface (`shared` ∪ role). ## Type Parameters ### C `C` *extends* [`Contract`](../../core/interfaces/Contract.md) ### R `R` *extends* [`RoleOf`](../../core/type-aliases/RoleOf.md)<`C`> --- --- url: >- https://super-line.dogar.biz/reference/@super-line/client/type-aliases/SuperLineClient.md --- [Documentation](../../../index.md) / [@super-line/client](../index.md) / SuperLineClient # Type Alias: SuperLineClient\ > **SuperLineClient**<`C`, `R`> = [`ClientMethods`](ClientMethods.md)<`C`, `R`> & `object` Defined in: [index.ts:69](https://github.com/mertdogar/super-line/blob/130bd9adcf4898b57c097e3121e358d596527fa2/packages/client/src/index.ts#L69) A typed client proxy, narrowed to role `R`'s effective surface (`shared` ∪ `R`). Call requests as methods; listen with `on`; subscribe to topics with `subscribe`. ## Type Declaration ### connected > `readonly` **connected**: `boolean` Whether the socket is currently open. ### role > `readonly` **role**: `R` This client's role. ### close() > **close**(): `void` Close the connection and stop reconnecting. #### Returns `void` ### implement() > **implement**(`handlers`): `void` Register handlers answering server→client requests. Throw a `SuperLineError` for a typed failure. #### Parameters ##### handlers [`ServerHandlers`](ServerHandlers.md)<`C`, `R`> #### Returns `void` ### on() > **on**<`E`>(`event`, `handler`): () => `void` Listen for a server-pushed event. Returns an unsubscribe function. #### Type Parameters ##### E `E` *extends* `string` | `number` | `symbol` #### Parameters ##### event `E` ##### handler (`data`) => `void` #### Returns () => `void` ### store() > **store**(`name`): [`ClientStoreHandle`](../interfaces/ClientStoreHandle.md) Client-side handle for a configured Store (`client.store('scene').open(id)`). Throws if the name isn't configured. #### Parameters ##### name `string` #### Returns [`ClientStoreHandle`](../interfaces/ClientStoreHandle.md) ### subscribe() > **subscribe**<`T`>(`topic`, `handler`): [`Subscription`](../interfaces/Subscription.md) Subscribe to a topic (auto re-subscribes on reconnect). Await `.ready` to confirm. #### Type Parameters ##### T `T` *extends* `string` | `number` | `symbol` #### Parameters ##### topic `T` ##### handler (`data`) => `void` #### Returns [`Subscription`](../interfaces/Subscription.md) ## Type Parameters ### C `C` *extends* [`Contract`](../../core/interfaces/Contract.md) ### R `R` *extends* [`RoleOf`](../../core/type-aliases/RoleOf.md)<`C`> --- --- url: >- https://super-line.dogar.biz/reference/@super-line/client/functions/backoffDelay.md --- [Documentation](../../../index.md) / [@super-line/client](../index.md) / backoffDelay # Function: backoffDelay() > **backoffDelay**(`attempt`, `opts`): `number` Defined in: [backoff.ts:19](https://github.com/mertdogar/super-line/blob/130bd9adcf4898b57c097e3121e358d596527fa2/packages/client/src/backoff.ts#L19) Exponential backoff with full jitter: a random delay in `[raw/2, raw]`, where `raw = min(maxMs, baseMs * factor ** attempt)`. A pure function — easy to unit-test. ## Parameters ### attempt `number` 0-based retry attempt. ### opts [`BackoffOptions`](../interfaces/BackoffOptions.md) backoff tuning. ## Returns `number` the delay in milliseconds before the next attempt. --- --- url: >- https://super-line.dogar.biz/reference/@super-line/client/functions/createSuperLineClient.md --- [Documentation](../../../index.md) / [@super-line/client](../index.md) / createSuperLineClient # Function: createSuperLineClient() > **createSuperLineClient**<`C`, `R`>(`contract`, `opts`): [`SuperLineClient`](../type-aliases/SuperLineClient.md)<`C`, `R`> Defined in: [index.ts:207](https://github.com/mertdogar/super-line/blob/130bd9adcf4898b57c097e3121e358d596527fa2/packages/client/src/index.ts#L207) Create a typed client for role `R`. Connects immediately and reconnects on its own. ## Type Parameters ### C `C` *extends* [`Contract`](../../core/interfaces/Contract.md) ### R `R` *extends* `string` ## Parameters ### contract `C` the shared contract. ### opts [`SuperLineClientOptions`](../interfaces/SuperLineClientOptions.md)<`C`, `R`> client options; `url` and `role` are required. ## Returns [`SuperLineClient`](../type-aliases/SuperLineClient.md)<`C`, `R`> a [SuperLineClient](../type-aliases/SuperLineClient.md) proxy narrowed to the role's surface. ## Example ```ts const client = createSuperLineClient(api, { transport: webSocketClientTransport({ url: 'ws://localhost:3000' }), role: 'user', params: { token }, }) client.on('message', (m) => console.log(m.text)) const out = await client.send({ text: 'hi' }) // throws SuperLineError on failure ``` --- --- url: 'https://super-line.dogar.biz/reference/@super-line/core.md' --- [Documentation](../../index.md) / @super-line/core # @super-line/core Shared core for [**super-line**](https://mertdogar.github.io/super-line/) — end-to-end typesafe WebSockets for TypeScript. This package holds the pieces both ends import: `defineContract`, runtime validation, the `SuperLineError` model, and the `Serializer` / `Adapter` interfaces. ```bash pnpm add @super-line/core zod ``` ```ts import { z } from 'zod' import { defineContract } from '@super-line/core' export const api = defineContract({ shared: { serverToClient: { message: { payload: z.object({ text: z.string() }) } }, }, roles: { user: { clientToServer: { send: { input: z.object({ text: z.string() }), output: z.object({ id: z.string() }) }, }, }, }, }) ``` The contract is split by **direction** (`clientToServer` / `serverToClient`) and scoped by **role**, then implemented by [`@super-line/server`](https://www.npmjs.com/package/@super-line/server) and called by [`@super-line/client`](https://www.npmjs.com/package/@super-line/client). * 📖 Docs: * 📚 The contract model: * 🧩 Source: MIT © Mert ## Classes * [SuperLineError](classes/SuperLineError.md) ## Interfaces * [Adapter](interfaces/Adapter.md) * [ClientStore](interfaces/ClientStore.md) * [ClientTransport](interfaces/ClientTransport.md) * [ConnDescriptor](interfaces/ConnDescriptor.md) * [ConnView](interfaces/ConnView.md) * [Contract](interfaces/Contract.md) * [Directional](interfaces/Directional.md) * [ErrFrame](interfaces/ErrFrame.md) * [EvtFrame](interfaces/EvtFrame.md) * [Handshake](interfaces/Handshake.md) * [InspectedContract](interfaces/InspectedContract.md) * [InspectedDirectional](interfaces/InspectedDirectional.md) * [InspectedMessage](interfaces/InspectedMessage.md) * [NodeStat](interfaces/NodeStat.md) * [NodeView](interfaces/NodeView.md) * [Perms](interfaces/Perms.md) * [PingFrame](interfaces/PingFrame.md) * [PongFrame](interfaces/PongFrame.md) * [PresenceStore](interfaces/PresenceStore.md) * [PubFrame](interfaces/PubFrame.md) * [RawConn](interfaces/RawConn.md) * [ReqFrame](interfaces/ReqFrame.md) * [RequestDef](interfaces/RequestDef.md) * [ResFrame](interfaces/ResFrame.md) * [Resource](interfaces/Resource.md) * [ResourceReplica](interfaces/ResourceReplica.md) * [RoleBlock](interfaces/RoleBlock.md) * [SChangeFrame](interfaces/SChangeFrame.md) * [SCloseFrame](interfaces/SCloseFrame.md) * [Serializer](interfaces/Serializer.md) * [SErrFrame](interfaces/SErrFrame.md) * [ServerMessageDef](interfaces/ServerMessageDef.md) * [ServerRequestDef](interfaces/ServerRequestDef.md) * [ServerStore](interfaces/ServerStore.md) * [ServerTransport](interfaces/ServerTransport.md) * [SOpenFrame](interfaces/SOpenFrame.md) * [SReadFrame](interfaces/SReadFrame.md) * [SReqFrame](interfaces/SReqFrame.md) * [SResFrame](interfaces/SResFrame.md) * [StoreChange](interfaces/StoreChange.md) * [SubFrame](interfaces/SubFrame.md) * [SWriteFrame](interfaces/SWriteFrame.md) * [UnsubFrame](interfaces/UnsubFrame.md) ## Type Aliases * [AccessRules](type-aliases/AccessRules.md) * [AnyData](type-aliases/AnyData.md) * [AuthOutcome](type-aliases/AuthOutcome.md) * [ClientFrame](type-aliases/ClientFrame.md) * [ClientInput](type-aliases/ClientInput.md) * [DataOf](type-aliases/DataOf.md) * [EmitData](type-aliases/EmitData.md) * [ErrorCode](type-aliases/ErrorCode.md) * [EventData](type-aliases/EventData.md) * [Events](type-aliases/Events.md) * [Frame](type-aliases/Frame.md) * [InferIn](type-aliases/InferIn.md) * [InferOut](type-aliases/InferOut.md) * [InspectorEvent](type-aliases/InspectorEvent.md) * [MessageFlavor](type-aliases/MessageFlavor.md) * [Output](type-aliases/Output.md) * [Principal](type-aliases/Principal.md) * [Requests](type-aliases/Requests.md) * [RoleOf](type-aliases/RoleOf.md) * [RoleRequests](type-aliases/RoleRequests.md) * [RoleTopics](type-aliases/RoleTopics.md) * [Schema](type-aliases/Schema.md) * [SchemaConverter](type-aliases/SchemaConverter.md) * [ServerEntry](type-aliases/ServerEntry.md) * [ServerFrame](type-aliases/ServerFrame.md) * [ServerInput](type-aliases/ServerInput.md) * [ServerMessages](type-aliases/ServerMessages.md) * [ServerRequests](type-aliases/ServerRequests.md) * [SharedEvents](type-aliases/SharedEvents.md) * [SharedRequests](type-aliases/SharedRequests.md) * [SharedServerRequests](type-aliases/SharedServerRequests.md) * [SharedTopics](type-aliases/SharedTopics.md) * [SuperLineErrorCode](type-aliases/SuperLineErrorCode.md) * [Topics](type-aliases/Topics.md) ## Variables * [INSPECTOR\_ROLE](variables/INSPECTOR_ROLE.md) * [INSPECTOR\_SUBPROTOCOL](variables/INSPECTOR_SUBPROTOCOL.md) * [InspectorContract](variables/InspectorContract.md) * [jsonSerializer](variables/jsonSerializer.md) * [PROTOCOL](variables/PROTOCOL.md) ## Functions * [classifyContract](functions/classifyContract.md) * [defineContract](functions/defineContract.md) * [validate](functions/validate.md) * [validateSync](functions/validateSync.md) --- --- url: >- https://super-line.dogar.biz/reference/@super-line/core/classes/SuperLineError.md --- [Documentation](../../../index.md) / [@super-line/core](../index.md) / SuperLineError # Class: SuperLineError\ Defined in: [packages/core/src/errors.ts:20](https://github.com/mertdogar/super-line/blob/130bd9adcf4898b57c097e3121e358d596527fa2/packages/core/src/errors.ts#L20) The error type carried end-to-end. Throw one from a handler and the client's promise rejects with the same `code` (and optional `data`). Unknown throws become `INTERNAL` so server internals aren't leaked. ## Extends * `Error` ## Type Parameters ### Data `Data` = `unknown` ## Constructors ### Constructor > **new SuperLineError**<`Data`>(`code`, `message?`, `data?`): `SuperLineError`<`Data`> Defined in: [packages/core/src/errors.ts:31](https://github.com/mertdogar/super-line/blob/130bd9adcf4898b57c097e3121e358d596527fa2/packages/core/src/errors.ts#L31) #### Parameters ##### code [`ErrorCode`](../type-aliases/ErrorCode.md) a [SuperLineErrorCode](../type-aliases/SuperLineErrorCode.md) or custom string. ##### message? `string` human-readable message (defaults to `code`). ##### data? `Data` optional structured payload delivered to the client. #### Returns `SuperLineError`<`Data`> #### Overrides `Error.constructor` ## Properties ### cause? > `optional` **cause?**: `unknown` Defined in: node\_modules/.pnpm/typescript@5.9.3/node\_modules/typescript/lib/lib.es2022.error.d.ts:26 #### Inherited from `Error.cause` *** ### code > `readonly` **code**: [`ErrorCode`](../type-aliases/ErrorCode.md) Defined in: [packages/core/src/errors.ts:22](https://github.com/mertdogar/super-line/blob/130bd9adcf4898b57c097e3121e358d596527fa2/packages/core/src/errors.ts#L22) The typed error code (e.g. `'FORBIDDEN'`), available on the client. *** ### data? > `readonly` `optional` **data?**: `Data` Defined in: [packages/core/src/errors.ts:24](https://github.com/mertdogar/super-line/blob/130bd9adcf4898b57c097e3121e358d596527fa2/packages/core/src/errors.ts#L24) Optional structured data attached to the error, delivered to the client. *** ### message > **message**: `string` Defined in: node\_modules/.pnpm/typescript@5.9.3/node\_modules/typescript/lib/lib.es5.d.ts:1077 #### Inherited from `Error.message` *** ### name > **name**: `string` Defined in: node\_modules/.pnpm/typescript@5.9.3/node\_modules/typescript/lib/lib.es5.d.ts:1076 #### Inherited from `Error.name` *** ### stack? > `optional` **stack?**: `string` Defined in: node\_modules/.pnpm/typescript@5.9.3/node\_modules/typescript/lib/lib.es5.d.ts:1078 #### Inherited from `Error.stack` *** ### stackTraceLimit > `static` **stackTraceLimit**: `number` Defined in: node\_modules/.pnpm/@types+node@22.19.21/node\_modules/@types/node/globals.d.ts:68 The `Error.stackTraceLimit` property specifies the number of stack frames collected by a stack trace (whether generated by `new Error().stack` or `Error.captureStackTrace(obj)`). The default value is `10` but may be set to any valid JavaScript number. Changes will affect any stack trace captured *after* the value has been changed. If set to a non-number value, or set to a negative number, stack traces will not capture any frames. #### Inherited from `Error.stackTraceLimit` ## Methods ### captureStackTrace() > `static` **captureStackTrace**(`targetObject`, `constructorOpt?`): `void` Defined in: node\_modules/.pnpm/@types+node@22.19.21/node\_modules/@types/node/globals.d.ts:52 Creates a `.stack` property on `targetObject`, which when accessed returns a string representing the location in the code at which `Error.captureStackTrace()` was called. ```js const myObject = {}; Error.captureStackTrace(myObject); myObject.stack; // Similar to `new Error().stack` ``` The first line of the trace will be prefixed with `${myObject.name}: ${myObject.message}`. The optional `constructorOpt` argument accepts a function. If given, all frames above `constructorOpt`, including `constructorOpt`, will be omitted from the generated stack trace. The `constructorOpt` argument is useful for hiding implementation details of error generation from the user. For instance: ```js function a() { b(); } function b() { c(); } function c() { // Create an error without stack trace to avoid calculating the stack trace twice. const { stackTraceLimit } = Error; Error.stackTraceLimit = 0; const error = new Error(); Error.stackTraceLimit = stackTraceLimit; // Capture the stack trace above function b Error.captureStackTrace(error, b); // Neither function c, nor b is included in the stack trace throw error; } a(); ``` #### Parameters ##### targetObject `object` ##### constructorOpt? `Function` #### Returns `void` #### Inherited from `Error.captureStackTrace` *** ### prepareStackTrace() > `static` **prepareStackTrace**(`err`, `stackTraces`): `any` Defined in: node\_modules/.pnpm/@types+node@22.19.21/node\_modules/@types/node/globals.d.ts:56 #### Parameters ##### err `Error` ##### stackTraces `CallSite`\[] #### Returns `any` #### See https://v8.dev/docs/stack-trace-api#customizing-stack-traces #### Inherited from `Error.prepareStackTrace` --- --- url: 'https://super-line.dogar.biz/reference/@super-line/core/interfaces/Adapter.md' --- [Documentation](../../../index.md) / [@super-line/core](../index.md) / Adapter # Interface: Adapter Defined in: [packages/core/src/adapter.ts:10](https://github.com/mertdogar/super-line/blob/130bd9adcf4898b57c097e3121e358d596527fa2/packages/core/src/adapter.ts#L10) Cross-node fan-out seam. Rooms, topics, and the cluster event bus all compile down to channel pub/sub. A node subscribes to a channel only while it has a local member, and publishes always go through the adapter (the in-memory adapter loops back), so a node delivers to its local members on receipt — one code path, no double-send. The default is a per-server in-memory adapter; use `@super-line/adapter-redis` to fan out across processes. ## Properties ### presence? > `optional` **presence?**: [`PresenceStore`](PresenceStore.md) Defined in: [packages/core/src/adapter.ts:26](https://github.com/mertdogar/super-line/blob/130bd9adcf4898b57c097e3121e358d596527fa2/packages/core/src/adapter.ts#L26) Optional cluster-wide presence directory. Powers `srv.cluster.*` and `srv.isOnline`. The in-memory and Redis adapters implement it; cluster queries throw a clear error on an adapter that doesn't. ## Methods ### close()? > `optional` **close**(): `void` | `Promise`<`void`> Defined in: [packages/core/src/adapter.ts:20](https://github.com/mertdogar/super-line/blob/130bd9adcf4898b57c097e3121e358d596527fa2/packages/core/src/adapter.ts#L20) Optional teardown (e.g. close Redis connections). #### Returns `void` | `Promise`<`void`> *** ### onMessage() > **onMessage**(`handler`): `void` Defined in: [packages/core/src/adapter.ts:18](https://github.com/mertdogar/super-line/blob/130bd9adcf4898b57c097e3121e358d596527fa2/packages/core/src/adapter.ts#L18) Register the handler invoked for each message on a subscribed channel. #### Parameters ##### handler (`channel`, `payload`) => `void` #### Returns `void` *** ### publish() > **publish**(`channel`, `payload`): `void` | `Promise`<`void`> Defined in: [packages/core/src/adapter.ts:16](https://github.com/mertdogar/super-line/blob/130bd9adcf4898b57c097e3121e358d596527fa2/packages/core/src/adapter.ts#L16) Publish an encoded payload to `channel` (delivered to every subscribed node). #### Parameters ##### channel `string` ##### payload `string` | `Uint8Array`<`ArrayBufferLike`> #### Returns `void` | `Promise`<`void`> *** ### subscribe() > **subscribe**(`channel`): `void` | `Promise`<`void`> Defined in: [packages/core/src/adapter.ts:12](https://github.com/mertdogar/super-line/blob/130bd9adcf4898b57c097e3121e358d596527fa2/packages/core/src/adapter.ts#L12) Start receiving messages published to `channel`. #### Parameters ##### channel `string` #### Returns `void` | `Promise`<`void`> *** ### unsubscribe() > **unsubscribe**(`channel`): `void` | `Promise`<`void`> Defined in: [packages/core/src/adapter.ts:14](https://github.com/mertdogar/super-line/blob/130bd9adcf4898b57c097e3121e358d596527fa2/packages/core/src/adapter.ts#L14) Stop receiving messages for `channel`. #### Parameters ##### channel `string` #### Returns `void` | `Promise`<`void`> --- --- url: >- https://super-line.dogar.biz/reference/@super-line/core/interfaces/ClientStore.md --- [Documentation](../../../index.md) / [@super-line/core](../index.md) / ClientStore # Interface: ClientStore Defined in: [packages/core/src/store.ts:80](https://github.com/mertdogar/super-line/blob/130bd9adcf4898b57c097e3121e358d596527fa2/packages/core/src/store.ts#L80) The client half of a Store pair: a reactive local replica per opened Resource. A last-writer-wins client wraps a plain value; a CRDT client wraps super-store's `StoreValue` and merges deltas. ## Properties ### origin > `readonly` **origin**: `string` Defined in: [packages/core/src/store.ts:82](https://github.com/mertdogar/super-line/blob/130bd9adcf4898b57c097e3121e358d596527fa2/packages/core/src/store.ts#L82) This client's per-writer id, stamped as [StoreChange.origin](StoreChange.md#origin) for echo-break. ## Methods ### close()? > `optional` **close**(): `void` Defined in: [packages/core/src/store.ts:86](https://github.com/mertdogar/super-line/blob/130bd9adcf4898b57c097e3121e358d596527fa2/packages/core/src/store.ts#L86) Release any resources held by the store. #### Returns `void` *** ### open() > **open**(`id`): [`ResourceReplica`](ResourceReplica.md) Defined in: [packages/core/src/store.ts:84](https://github.com/mertdogar/super-line/blob/130bd9adcf4898b57c097e3121e358d596527fa2/packages/core/src/store.ts#L84) Open a reactive replica for one Resource. #### Parameters ##### id `string` #### Returns [`ResourceReplica`](ResourceReplica.md) --- --- url: >- https://super-line.dogar.biz/reference/@super-line/core/interfaces/ClientTransport.md --- [Documentation](../../../index.md) / [@super-line/core](../index.md) / ClientTransport # Interface: ClientTransport Defined in: [packages/core/src/transport.ts:68](https://github.com/mertdogar/super-line/blob/130bd9adcf4898b57c097e3121e358d596527fa2/packages/core/src/transport.ts#L68) Client side: dial the server, encoding `handshakeParams` (role + params) in the transport's native form. ## Methods ### connect() > **connect**(`handshakeParams`, `hooks`): [`RawConn`](RawConn.md) Defined in: [packages/core/src/transport.ts:69](https://github.com/mertdogar/super-line/blob/130bd9adcf4898b57c097e3121e358d596527fa2/packages/core/src/transport.ts#L69) #### Parameters ##### handshakeParams `Record`<`string`, `string`> ##### hooks ###### onClose ###### onDrain ###### onMessage ###### onOpen #### Returns [`RawConn`](RawConn.md) --- --- url: >- https://super-line.dogar.biz/reference/@super-line/core/interfaces/ConnDescriptor.md --- [Documentation](../../../index.md) / [@super-line/core](../index.md) / ConnDescriptor # Interface: ConnDescriptor Defined in: [packages/core/src/adapter.ts:30](https://github.com/mertdogar/super-line/blob/130bd9adcf4898b57c097e3121e358d596527fa2/packages/core/src/adapter.ts#L30) A serializable snapshot of a connection, shared cluster-wide via the [PresenceStore](PresenceStore.md). ## Indexable > \[`extra`: `string`]: `unknown` Extra fields contributed by the server's `describeConn` hook. ## Properties ### connectedAt > **connectedAt**: `number` Defined in: [packages/core/src/adapter.ts:40](https://github.com/mertdogar/super-line/blob/130bd9adcf4898b57c097e3121e358d596527fa2/packages/core/src/adapter.ts#L40) When the connection was accepted (`Date.now()`). *** ### id > **id**: `string` Defined in: [packages/core/src/adapter.ts:32](https://github.com/mertdogar/super-line/blob/130bd9adcf4898b57c097e3121e358d596527fa2/packages/core/src/adapter.ts#L32) The connection's server-assigned id. *** ### nodeId > **nodeId**: `string` Defined in: [packages/core/src/adapter.ts:36](https://github.com/mertdogar/super-line/blob/130bd9adcf4898b57c097e3121e358d596527fa2/packages/core/src/adapter.ts#L36) The node that holds this connection. *** ### nodeName > **nodeName**: `string` Defined in: [packages/core/src/adapter.ts:38](https://github.com/mertdogar/super-line/blob/130bd9adcf4898b57c097e3121e358d596527fa2/packages/core/src/adapter.ts#L38) The node's friendly name (defaults to a short slice of `nodeId`). *** ### role > **role**: `string` Defined in: [packages/core/src/adapter.ts:34](https://github.com/mertdogar/super-line/blob/130bd9adcf4898b57c097e3121e358d596527fa2/packages/core/src/adapter.ts#L34) The connection's role. *** ### rooms > **rooms**: `string`\[] Defined in: [packages/core/src/adapter.ts:44](https://github.com/mertdogar/super-line/blob/130bd9adcf4898b57c097e3121e358d596527fa2/packages/core/src/adapter.ts#L44) Room memberships (topics and node-local `lastPongAt` are not included). *** ### transport? > `optional` **transport?**: `string` Defined in: [packages/core/src/adapter.ts:49](https://github.com/mertdogar/super-line/blob/130bd9adcf4898b57c097e3121e358d596527fa2/packages/core/src/adapter.ts#L49) The client↔server transport (wire) this connection was accepted on: `'websocket' | 'sse' | 'longpoll' | 'libp2p' | 'loopback'`. Absent on conns from older nodes. *** ### userId? > `optional` **userId?**: `string` Defined in: [packages/core/src/adapter.ts:42](https://github.com/mertdogar/super-line/blob/130bd9adcf4898b57c097e3121e358d596527fa2/packages/core/src/adapter.ts#L42) The stable user key from the server's `identify` hook, if any. --- --- url: 'https://super-line.dogar.biz/reference/@super-line/core/interfaces/ConnView.md' --- [Documentation](../../../index.md) / [@super-line/core](../index.md) / ConnView # Interface: ConnView Defined in: [packages/core/src/inspector.ts:50](https://github.com/mertdogar/super-line/blob/130bd9adcf4898b57c097e3121e358d596527fa2/packages/core/src/inspector.ts#L50) A connection's detail — what `getConn` returns. ctx/data are node-local and best-effort safe-serialized. ## Properties ### ctx? > `optional` **ctx?**: `unknown` Defined in: [packages/core/src/inspector.ts:53](https://github.com/mertdogar/super-line/blob/130bd9adcf4898b57c097e3121e358d596527fa2/packages/core/src/inspector.ts#L53) Safe-serialized auth ctx; present only when the conn is on the queried node. *** ### ctxAvailable > **ctxAvailable**: `boolean` Defined in: [packages/core/src/inspector.ts:57](https://github.com/mertdogar/super-line/blob/130bd9adcf4898b57c097e3121e358d596527fa2/packages/core/src/inspector.ts#L57) Whether ctx/data could be read (false for conns on another node). *** ### data? > `optional` **data?**: `unknown` Defined in: [packages/core/src/inspector.ts:55](https://github.com/mertdogar/super-line/blob/130bd9adcf4898b57c097e3121e358d596527fa2/packages/core/src/inspector.ts#L55) Safe-serialized `conn.data`; present only when the conn is on the queried node. *** ### descriptor > **descriptor**: [`ConnDescriptor`](ConnDescriptor.md) Defined in: [packages/core/src/inspector.ts:51](https://github.com/mertdogar/super-line/blob/130bd9adcf4898b57c097e3121e358d596527fa2/packages/core/src/inspector.ts#L51) --- --- url: 'https://super-line.dogar.biz/reference/@super-line/core/interfaces/Contract.md' --- [Documentation](../../../index.md) / [@super-line/core](../index.md) / Contract # Interface: Contract Defined in: [packages/core/src/contract.ts:64](https://github.com/mertdogar/super-line/blob/130bd9adcf4898b57c097e3121e358d596527fa2/packages/core/src/contract.ts#L64) The single source of truth, imported by both server and client. Split by **direction** and scoped by **role**: a `shared` base every role inherits, plus one block per role. ## Properties ### roles > **roles**: `Record`<`string`, [`RoleBlock`](RoleBlock.md)> Defined in: [packages/core/src/contract.ts:68](https://github.com/mertdogar/super-line/blob/130bd9adcf4898b57c097e3121e358d596527fa2/packages/core/src/contract.ts#L68) Per-role surfaces. A connection's role selects which one (plus `shared`) it sees. *** ### shared? > `optional` **shared?**: [`Directional`](Directional.md) Defined in: [packages/core/src/contract.ts:66](https://github.com/mertdogar/super-line/blob/130bd9adcf4898b57c097e3121e358d596527fa2/packages/core/src/contract.ts#L66) Surface common to every role (merged into each role's effective surface). --- --- url: >- https://super-line.dogar.biz/reference/@super-line/core/interfaces/Directional.md --- [Documentation](../../../index.md) / [@super-line/core](../index.md) / Directional # Interface: Directional Defined in: [packages/core/src/contract.ts:46](https://github.com/mertdogar/super-line/blob/130bd9adcf4898b57c097e3121e358d596527fa2/packages/core/src/contract.ts#L46) The two directions within a `shared` or role block. ## Extended by * [`RoleBlock`](RoleBlock.md) ## Properties ### clientToServer? > `optional` **clientToServer?**: `Record`<`string`, [`RequestDef`](RequestDef.md)> Defined in: [packages/core/src/contract.ts:48](https://github.com/mertdogar/super-line/blob/130bd9adcf4898b57c097e3121e358d596527fa2/packages/core/src/contract.ts#L48) Requests this side may call (client→server). *** ### serverToClient? > `optional` **serverToClient?**: `Record`<`string`, [`ServerEntry`](../type-aliases/ServerEntry.md)> Defined in: [packages/core/src/contract.ts:50](https://github.com/mertdogar/super-line/blob/130bd9adcf4898b57c097e3121e358d596527fa2/packages/core/src/contract.ts#L50) Events, topics, and server→client requests this side may receive. --- --- url: 'https://super-line.dogar.biz/reference/@super-line/core/interfaces/ErrFrame.md' --- [Documentation](../../../index.md) / [@super-line/core](../index.md) / ErrFrame # Interface: ErrFrame Defined in: [packages/core/src/wire.ts:92](https://github.com/mertdogar/super-line/blob/130bd9adcf4898b57c097e3121e358d596527fa2/packages/core/src/wire.ts#L92) ## Properties ### code > **code**: `string` Defined in: [packages/core/src/wire.ts:95](https://github.com/mertdogar/super-line/blob/130bd9adcf4898b57c097e3121e358d596527fa2/packages/core/src/wire.ts#L95) *** ### d? > `optional` **d?**: `unknown` Defined in: [packages/core/src/wire.ts:97](https://github.com/mertdogar/super-line/blob/130bd9adcf4898b57c097e3121e358d596527fa2/packages/core/src/wire.ts#L97) *** ### i? > `optional` **i?**: `number` Defined in: [packages/core/src/wire.ts:94](https://github.com/mertdogar/super-line/blob/130bd9adcf4898b57c097e3121e358d596527fa2/packages/core/src/wire.ts#L94) *** ### m > **m**: `string` Defined in: [packages/core/src/wire.ts:96](https://github.com/mertdogar/super-line/blob/130bd9adcf4898b57c097e3121e358d596527fa2/packages/core/src/wire.ts#L96) *** ### t > **t**: `"err"` Defined in: [packages/core/src/wire.ts:93](https://github.com/mertdogar/super-line/blob/130bd9adcf4898b57c097e3121e358d596527fa2/packages/core/src/wire.ts#L93) --- --- url: 'https://super-line.dogar.biz/reference/@super-line/core/interfaces/EvtFrame.md' --- [Documentation](../../../index.md) / [@super-line/core](../index.md) / EvtFrame # Interface: EvtFrame Defined in: [packages/core/src/wire.ts:99](https://github.com/mertdogar/super-line/blob/130bd9adcf4898b57c097e3121e358d596527fa2/packages/core/src/wire.ts#L99) ## Properties ### d > **d**: `unknown` Defined in: [packages/core/src/wire.ts:102](https://github.com/mertdogar/super-line/blob/130bd9adcf4898b57c097e3121e358d596527fa2/packages/core/src/wire.ts#L102) *** ### e > **e**: `string` Defined in: [packages/core/src/wire.ts:101](https://github.com/mertdogar/super-line/blob/130bd9adcf4898b57c097e3121e358d596527fa2/packages/core/src/wire.ts#L101) *** ### t > **t**: `"evt"` Defined in: [packages/core/src/wire.ts:100](https://github.com/mertdogar/super-line/blob/130bd9adcf4898b57c097e3121e358d596527fa2/packages/core/src/wire.ts#L100) --- --- url: >- https://super-line.dogar.biz/reference/@super-line/core/interfaces/Handshake.md --- [Documentation](../../../index.md) / [@super-line/core](../index.md) / Handshake # Interface: Handshake Defined in: [packages/core/src/transport.ts:31](https://github.com/mertdogar/super-line/blob/130bd9adcf4898b57c097e3121e358d596527fa2/packages/core/src/transport.ts#L31) The normalized handshake handed to `authenticate`, replacing the raw `IncomingMessage`. Each transport fills what it has: ws/sse populate `headers`/`query`; libp2p/webrtc populate `peer`. `raw` is the transport-specific escape hatch. ## Properties ### headers > **headers**: `Record`<`string`, `string` | `string`\[] | `undefined`> Defined in: [packages/core/src/transport.ts:35](https://github.com/mertdogar/super-line/blob/130bd9adcf4898b57c097e3121e358d596527fa2/packages/core/src/transport.ts#L35) Request headers (ws/sse fill these; peer transports leave them sparse). *** ### peer? > `optional` **peer?**: `object` Defined in: [packages/core/src/transport.ts:39](https://github.com/mertdogar/super-line/blob/130bd9adcf4898b57c097e3121e358d596527fa2/packages/core/src/transport.ts#L39) Peer identity, for transports that authenticate one (libp2p/webrtc). #### addr? > `optional` **addr?**: `string` #### id > **id**: `string` *** ### query > **query**: `Record`<`string`, `string`> Defined in: [packages/core/src/transport.ts:37](https://github.com/mertdogar/super-line/blob/130bd9adcf4898b57c097e3121e358d596527fa2/packages/core/src/transport.ts#L37) Role + params, decoded uniformly (WS reads them from the URL query string). *** ### raw > **raw**: `unknown` Defined in: [packages/core/src/transport.ts:41](https://github.com/mertdogar/super-line/blob/130bd9adcf4898b57c097e3121e358d596527fa2/packages/core/src/transport.ts#L41) Escape hatch: the `IncomingMessage` for WS, the signaling payload for libp2p, etc. *** ### transport > **transport**: `string` Defined in: [packages/core/src/transport.ts:33](https://github.com/mertdogar/super-line/blob/130bd9adcf4898b57c097e3121e358d596527fa2/packages/core/src/transport.ts#L33) Transport id, e.g. `'websocket'` | `'loopback'` | `'sse'` | `'libp2p'`. --- --- url: >- https://super-line.dogar.biz/reference/@super-line/core/interfaces/InspectedContract.md --- [Documentation](../../../index.md) / [@super-line/core](../index.md) / InspectedContract # Interface: InspectedContract Defined in: [packages/core/src/inspector.ts:36](https://github.com/mertdogar/super-line/blob/130bd9adcf4898b57c097e3121e358d596527fa2/packages/core/src/inspector.ts#L36) A serializable projection of a [Contract](Contract.md)'s structure — what `getContract` returns. ## Properties ### roles > **roles**: `Record`<`string`, [`InspectedDirectional`](InspectedDirectional.md)> Defined in: [packages/core/src/inspector.ts:38](https://github.com/mertdogar/super-line/blob/130bd9adcf4898b57c097e3121e358d596527fa2/packages/core/src/inspector.ts#L38) *** ### shared > **shared**: [`InspectedDirectional`](InspectedDirectional.md) Defined in: [packages/core/src/inspector.ts:37](https://github.com/mertdogar/super-line/blob/130bd9adcf4898b57c097e3121e358d596527fa2/packages/core/src/inspector.ts#L37) --- --- url: >- https://super-line.dogar.biz/reference/@super-line/core/interfaces/InspectedDirectional.md --- [Documentation](../../../index.md) / [@super-line/core](../index.md) / InspectedDirectional # Interface: InspectedDirectional Defined in: [packages/core/src/inspector.ts:30](https://github.com/mertdogar/super-line/blob/130bd9adcf4898b57c097e3121e358d596527fa2/packages/core/src/inspector.ts#L30) The two directions of a `shared` or role block, flattened for display. ## Properties ### clientToServer > **clientToServer**: [`InspectedMessage`](InspectedMessage.md)\[] Defined in: [packages/core/src/inspector.ts:31](https://github.com/mertdogar/super-line/blob/130bd9adcf4898b57c097e3121e358d596527fa2/packages/core/src/inspector.ts#L31) *** ### serverToClient > **serverToClient**: [`InspectedMessage`](InspectedMessage.md)\[] Defined in: [packages/core/src/inspector.ts:32](https://github.com/mertdogar/super-line/blob/130bd9adcf4898b57c097e3121e358d596527fa2/packages/core/src/inspector.ts#L32) --- --- url: >- https://super-line.dogar.biz/reference/@super-line/core/interfaces/InspectedMessage.md --- [Documentation](../../../index.md) / [@super-line/core](../index.md) / InspectedMessage # Interface: InspectedMessage Defined in: [packages/core/src/inspector.ts:16](https://github.com/mertdogar/super-line/blob/130bd9adcf4898b57c097e3121e358d596527fa2/packages/core/src/inspector.ts#L16) One message in an [InspectedContract](InspectedContract.md). Schemas are best-effort JSON Schema, omitted when unavailable. ## Properties ### flavor > **flavor**: [`MessageFlavor`](../type-aliases/MessageFlavor.md) Defined in: [packages/core/src/inspector.ts:20](https://github.com/mertdogar/super-line/blob/130bd9adcf4898b57c097e3121e358d596527fa2/packages/core/src/inspector.ts#L20) How the message is used. *** ### input? > `optional` **input?**: `unknown` Defined in: [packages/core/src/inspector.ts:22](https://github.com/mertdogar/super-line/blob/130bd9adcf4898b57c097e3121e358d596527fa2/packages/core/src/inspector.ts#L22) Best-effort JSON Schema of the request/server-request input. *** ### name > **name**: `string` Defined in: [packages/core/src/inspector.ts:18](https://github.com/mertdogar/super-line/blob/130bd9adcf4898b57c097e3121e358d596527fa2/packages/core/src/inspector.ts#L18) The message name (its key in the contract). *** ### output? > `optional` **output?**: `unknown` Defined in: [packages/core/src/inspector.ts:24](https://github.com/mertdogar/super-line/blob/130bd9adcf4898b57c097e3121e358d596527fa2/packages/core/src/inspector.ts#L24) Best-effort JSON Schema of the request/server-request output. *** ### payload? > `optional` **payload?**: `unknown` Defined in: [packages/core/src/inspector.ts:26](https://github.com/mertdogar/super-line/blob/130bd9adcf4898b57c097e3121e358d596527fa2/packages/core/src/inspector.ts#L26) Best-effort JSON Schema of an event/topic payload. --- --- url: 'https://super-line.dogar.biz/reference/@super-line/core/interfaces/NodeStat.md' --- [Documentation](../../../index.md) / [@super-line/core](../index.md) / NodeStat # Interface: NodeStat Defined in: [packages/core/src/adapter.ts:55](https://github.com/mertdogar/super-line/blob/130bd9adcf4898b57c097e3121e358d596527fa2/packages/core/src/adapter.ts#L55) Per-node aggregate, returned by [PresenceStore.topology](PresenceStore.md#topology). ## Properties ### alive > **alive**: `boolean` Defined in: [packages/core/src/adapter.ts:65](https://github.com/mertdogar/super-line/blob/130bd9adcf4898b57c097e3121e358d596527fa2/packages/core/src/adapter.ts#L65) Whether the node is currently live (heartbeat fresh). *** ### connections > **connections**: `number` Defined in: [packages/core/src/adapter.ts:61](https://github.com/mertdogar/super-line/blob/130bd9adcf4898b57c097e3121e358d596527fa2/packages/core/src/adapter.ts#L61) Number of connections on the node. *** ### nodeId > **nodeId**: `string` Defined in: [packages/core/src/adapter.ts:57](https://github.com/mertdogar/super-line/blob/130bd9adcf4898b57c097e3121e358d596527fa2/packages/core/src/adapter.ts#L57) The node's id. *** ### nodeName > **nodeName**: `string` Defined in: [packages/core/src/adapter.ts:59](https://github.com/mertdogar/super-line/blob/130bd9adcf4898b57c097e3121e358d596527fa2/packages/core/src/adapter.ts#L59) The node's friendly name (defaults to a short slice of `nodeId`). *** ### rooms > **rooms**: `number` Defined in: [packages/core/src/adapter.ts:63](https://github.com/mertdogar/super-line/blob/130bd9adcf4898b57c097e3121e358d596527fa2/packages/core/src/adapter.ts#L63) Number of distinct rooms with members on the node. --- --- url: 'https://super-line.dogar.biz/reference/@super-line/core/interfaces/NodeView.md' --- [Documentation](../../../index.md) / [@super-line/core](../index.md) / NodeView # Interface: NodeView Defined in: [packages/core/src/inspector.ts:42](https://github.com/mertdogar/super-line/blob/130bd9adcf4898b57c097e3121e358d596527fa2/packages/core/src/inspector.ts#L42) The connected node's local view — what `getNode` returns. ## Properties ### nodeId > **nodeId**: `string` Defined in: [packages/core/src/inspector.ts:43](https://github.com/mertdogar/super-line/blob/130bd9adcf4898b57c097e3121e358d596527fa2/packages/core/src/inspector.ts#L43) *** ### nodeName > **nodeName**: `string` Defined in: [packages/core/src/inspector.ts:44](https://github.com/mertdogar/super-line/blob/130bd9adcf4898b57c097e3121e358d596527fa2/packages/core/src/inspector.ts#L44) *** ### rooms > **rooms**: `string`\[] Defined in: [packages/core/src/inspector.ts:45](https://github.com/mertdogar/super-line/blob/130bd9adcf4898b57c097e3121e358d596527fa2/packages/core/src/inspector.ts#L45) *** ### topics > **topics**: `string`\[] Defined in: [packages/core/src/inspector.ts:46](https://github.com/mertdogar/super-line/blob/130bd9adcf4898b57c097e3121e358d596527fa2/packages/core/src/inspector.ts#L46) --- --- url: 'https://super-line.dogar.biz/reference/@super-line/core/interfaces/Perms.md' --- [Documentation](../../../index.md) / [@super-line/core](../index.md) / Perms # Interface: Perms Defined in: [packages/core/src/store.ts:18](https://github.com/mertdogar/super-line/blob/130bd9adcf4898b57c097e3121e358d596527fa2/packages/core/src/store.ts#L18) Per-principal capabilities on a Resource. ## Properties ### read > **read**: `boolean` Defined in: [packages/core/src/store.ts:19](https://github.com/mertdogar/super-line/blob/130bd9adcf4898b57c097e3121e358d596527fa2/packages/core/src/store.ts#L19) *** ### write > **write**: `boolean` Defined in: [packages/core/src/store.ts:20](https://github.com/mertdogar/super-line/blob/130bd9adcf4898b57c097e3121e358d596527fa2/packages/core/src/store.ts#L20) --- --- url: >- https://super-line.dogar.biz/reference/@super-line/core/interfaces/PingFrame.md --- [Documentation](../../../index.md) / [@super-line/core](../index.md) / PingFrame # Interface: PingFrame Defined in: [packages/core/src/wire.ts:40](https://github.com/mertdogar/super-line/blob/130bd9adcf4898b57c097e3121e358d596527fa2/packages/core/src/wire.ts#L40) ## Properties ### t > **t**: `"ping"` Defined in: [packages/core/src/wire.ts:41](https://github.com/mertdogar/super-line/blob/130bd9adcf4898b57c097e3121e358d596527fa2/packages/core/src/wire.ts#L41) --- --- url: >- https://super-line.dogar.biz/reference/@super-line/core/interfaces/PongFrame.md --- [Documentation](../../../index.md) / [@super-line/core](../index.md) / PongFrame # Interface: PongFrame Defined in: [packages/core/src/wire.ts:43](https://github.com/mertdogar/super-line/blob/130bd9adcf4898b57c097e3121e358d596527fa2/packages/core/src/wire.ts#L43) ## Properties ### t > **t**: `"pong"` Defined in: [packages/core/src/wire.ts:44](https://github.com/mertdogar/super-line/blob/130bd9adcf4898b57c097e3121e358d596527fa2/packages/core/src/wire.ts#L44) --- --- url: >- https://super-line.dogar.biz/reference/@super-line/core/interfaces/PresenceStore.md --- [Documentation](../../../index.md) / [@super-line/core](../index.md) / PresenceStore # Interface: PresenceStore Defined in: [packages/core/src/adapter.ts:73](https://github.com/mertdogar/super-line/blob/130bd9adcf4898b57c097e3121e358d596527fa2/packages/core/src/adapter.ts#L73) Cluster-wide presence directory: a query/addressbook layer kept in the shared substrate (in-memory bus or Redis). Live message delivery does NOT read this — it exists only to answer `srv.cluster.*` / `srv.isOnline`. ## Methods ### addRoom() > **addRoom**(`connId`, `room`): `void` | `Promise`<`void`> Defined in: [packages/core/src/adapter.ts:83](https://github.com/mertdogar/super-line/blob/130bd9adcf4898b57c097e3121e358d596527fa2/packages/core/src/adapter.ts#L83) Add a room to a connection's membership. #### Parameters ##### connId `string` ##### room `string` #### Returns `void` | `Promise`<`void`> *** ### beat() > **beat**(`nodeId`): `void` | `Promise`<`void`> Defined in: [packages/core/src/adapter.ts:79](https://github.com/mertdogar/super-line/blob/130bd9adcf4898b57c097e3121e358d596527fa2/packages/core/src/adapter.ts#L79) Refresh this node's liveness (heartbeat). #### Parameters ##### nodeId `string` #### Returns `void` | `Promise`<`void`> *** ### byUser() > **byUser**(`userId`): [`ConnDescriptor`](ConnDescriptor.md)\[] | `Promise`<[`ConnDescriptor`](ConnDescriptor.md)\[]> Defined in: [packages/core/src/adapter.ts:91](https://github.com/mertdogar/super-line/blob/130bd9adcf4898b57c097e3121e358d596527fa2/packages/core/src/adapter.ts#L91) Descriptors for a given user key. #### Parameters ##### userId `string` #### Returns [`ConnDescriptor`](ConnDescriptor.md)\[] | `Promise`<[`ConnDescriptor`](ConnDescriptor.md)\[]> *** ### clearNode() > **clearNode**(`nodeId`): `void` | `Promise`<`void`> Defined in: [packages/core/src/adapter.ts:81](https://github.com/mertdogar/super-line/blob/130bd9adcf4898b57c097e3121e358d596527fa2/packages/core/src/adapter.ts#L81) Remove all of a node's connections + liveness (graceful shutdown cleanup). #### Parameters ##### nodeId `string` #### Returns `void` | `Promise`<`void`> *** ### count() > **count**(): `number` | `Promise`<`number`> Defined in: [packages/core/src/adapter.ts:95](https://github.com/mertdogar/super-line/blob/130bd9adcf4898b57c097e3121e358d596527fa2/packages/core/src/adapter.ts#L95) Total live connection count across the cluster. #### Returns `number` | `Promise`<`number`> *** ### del() > **del**(`connId`): `void` | `Promise`<`void`> Defined in: [packages/core/src/adapter.ts:77](https://github.com/mertdogar/super-line/blob/130bd9adcf4898b57c097e3121e358d596527fa2/packages/core/src/adapter.ts#L77) Remove a connection's descriptor. #### Parameters ##### connId `string` #### Returns `void` | `Promise`<`void`> *** ### get() > **get**(`connId`): [`ConnDescriptor`](ConnDescriptor.md) | `Promise`<[`ConnDescriptor`](ConnDescriptor.md) | `undefined`> | `undefined` Defined in: [packages/core/src/adapter.ts:89](https://github.com/mertdogar/super-line/blob/130bd9adcf4898b57c097e3121e358d596527fa2/packages/core/src/adapter.ts#L89) One connection's descriptor, if present. #### Parameters ##### connId `string` #### Returns [`ConnDescriptor`](ConnDescriptor.md) | `Promise`<[`ConnDescriptor`](ConnDescriptor.md) | `undefined`> | `undefined` *** ### list() > **list**(): [`ConnDescriptor`](ConnDescriptor.md)\[] | `Promise`<[`ConnDescriptor`](ConnDescriptor.md)\[]> Defined in: [packages/core/src/adapter.ts:87](https://github.com/mertdogar/super-line/blob/130bd9adcf4898b57c097e3121e358d596527fa2/packages/core/src/adapter.ts#L87) All live connection descriptors across the cluster. #### Returns [`ConnDescriptor`](ConnDescriptor.md)\[] | `Promise`<[`ConnDescriptor`](ConnDescriptor.md)\[]> *** ### removeRoom() > **removeRoom**(`connId`, `room`): `void` | `Promise`<`void`> Defined in: [packages/core/src/adapter.ts:85](https://github.com/mertdogar/super-line/blob/130bd9adcf4898b57c097e3121e358d596527fa2/packages/core/src/adapter.ts#L85) Remove a room from a connection's membership. #### Parameters ##### connId `string` ##### room `string` #### Returns `void` | `Promise`<`void`> *** ### roomMembers() > **roomMembers**(`room`): [`ConnDescriptor`](ConnDescriptor.md)\[] | `Promise`<[`ConnDescriptor`](ConnDescriptor.md)\[]> Defined in: [packages/core/src/adapter.ts:93](https://github.com/mertdogar/super-line/blob/130bd9adcf4898b57c097e3121e358d596527fa2/packages/core/src/adapter.ts#L93) Descriptors that are members of `room`. #### Parameters ##### room `string` #### Returns [`ConnDescriptor`](ConnDescriptor.md)\[] | `Promise`<[`ConnDescriptor`](ConnDescriptor.md)\[]> *** ### set() > **set**(`descriptor`): `void` | `Promise`<`void`> Defined in: [packages/core/src/adapter.ts:75](https://github.com/mertdogar/super-line/blob/130bd9adcf4898b57c097e3121e358d596527fa2/packages/core/src/adapter.ts#L75) Record (or replace) a connection's descriptor. #### Parameters ##### descriptor [`ConnDescriptor`](ConnDescriptor.md) #### Returns `void` | `Promise`<`void`> *** ### topology() > **topology**(): [`NodeStat`](NodeStat.md)\[] | `Promise`<[`NodeStat`](NodeStat.md)\[]> Defined in: [packages/core/src/adapter.ts:97](https://github.com/mertdogar/super-line/blob/130bd9adcf4898b57c097e3121e358d596527fa2/packages/core/src/adapter.ts#L97) Per-node aggregates. #### Returns [`NodeStat`](NodeStat.md)\[] | `Promise`<[`NodeStat`](NodeStat.md)\[]> --- --- url: 'https://super-line.dogar.biz/reference/@super-line/core/interfaces/PubFrame.md' --- [Documentation](../../../index.md) / [@super-line/core](../index.md) / PubFrame # Interface: PubFrame Defined in: [packages/core/src/wire.ts:104](https://github.com/mertdogar/super-line/blob/130bd9adcf4898b57c097e3121e358d596527fa2/packages/core/src/wire.ts#L104) ## Properties ### c > **c**: `string` Defined in: [packages/core/src/wire.ts:106](https://github.com/mertdogar/super-line/blob/130bd9adcf4898b57c097e3121e358d596527fa2/packages/core/src/wire.ts#L106) *** ### d > **d**: `unknown` Defined in: [packages/core/src/wire.ts:107](https://github.com/mertdogar/super-line/blob/130bd9adcf4898b57c097e3121e358d596527fa2/packages/core/src/wire.ts#L107) *** ### i? > `optional` **i?**: `string` Defined in: [packages/core/src/wire.ts:108](https://github.com/mertdogar/super-line/blob/130bd9adcf4898b57c097e3121e358d596527fa2/packages/core/src/wire.ts#L108) *** ### t > **t**: `"pub"` Defined in: [packages/core/src/wire.ts:105](https://github.com/mertdogar/super-line/blob/130bd9adcf4898b57c097e3121e358d596527fa2/packages/core/src/wire.ts#L105) --- --- url: 'https://super-line.dogar.biz/reference/@super-line/core/interfaces/RawConn.md' --- [Documentation](../../../index.md) / [@super-line/core](../index.md) / RawConn # Interface: RawConn Defined in: [packages/core/src/transport.ts:9](https://github.com/mertdogar/super-line/blob/130bd9adcf4898b57c097e3121e358d596527fa2/packages/core/src/transport.ts#L9) A live logical connection, from the core's point of view. Symmetric across server + client. ## Properties ### writable > `readonly` **writable**: `boolean` Defined in: [packages/core/src/transport.ts:13](https://github.com/mertdogar/super-line/blob/130bd9adcf4898b57c097e3121e358d596527fa2/packages/core/src/transport.ts#L13) Whether a send will be accepted now (WS derives this from `readyState` + `bufferedAmount`). ## Methods ### close() > **close**(`code?`, `reason?`): `void` Defined in: [packages/core/src/transport.ts:21](https://github.com/mertdogar/super-line/blob/130bd9adcf4898b57c097e3121e358d596527fa2/packages/core/src/transport.ts#L21) Graceful close (close handshake when the transport has one). #### Parameters ##### code? `number` ##### reason? `string` #### Returns `void` *** ### onClose() > **onClose**(`cb`): `void` Defined in: [packages/core/src/transport.ts:17](https://github.com/mertdogar/super-line/blob/130bd9adcf4898b57c097e3121e358d596527fa2/packages/core/src/transport.ts#L17) The logical connection died. `code` is best-effort (1000 graceful / 1006 abnormal when the transport has none). #### Parameters ##### cb (`code`, `reason?`) => `void` #### Returns `void` *** ### onDrain() > **onDrain**(`cb`): `void` Defined in: [packages/core/src/transport.ts:19](https://github.com/mertdogar/super-line/blob/130bd9adcf4898b57c097e3121e358d596527fa2/packages/core/src/transport.ts#L19) The send buffer drained below the limit — safe to resume sending. #### Parameters ##### cb () => `void` #### Returns `void` *** ### onMessage() > **onMessage**(`cb`): `void` Defined in: [packages/core/src/transport.ts:15](https://github.com/mertdogar/super-line/blob/130bd9adcf4898b57c097e3121e358d596527fa2/packages/core/src/transport.ts#L15) Register the handler for inbound frames. The transport MUST normalize each to a `Uint8Array`. #### Parameters ##### cb (`bytes`) => `void` #### Returns `void` *** ### send() > **send**(`bytes`): `void` Defined in: [packages/core/src/transport.ts:11](https://github.com/mertdogar/super-line/blob/130bd9adcf4898b57c097e3121e358d596527fa2/packages/core/src/transport.ts#L11) Send already-encoded bytes. A no-op when not [RawConn.writable](#writable). #### Parameters ##### bytes `string` | `Uint8Array`<`ArrayBufferLike`> #### Returns `void` *** ### terminate() > **terminate**(): `void` Defined in: [packages/core/src/transport.ts:23](https://github.com/mertdogar/super-line/blob/130bd9adcf4898b57c097e3121e358d596527fa2/packages/core/src/transport.ts#L23) Hard close with no handshake — used by heartbeat reaping. #### Returns `void` --- --- url: 'https://super-line.dogar.biz/reference/@super-line/core/interfaces/ReqFrame.md' --- [Documentation](../../../index.md) / [@super-line/core](../index.md) / ReqFrame # Interface: ReqFrame Defined in: [packages/core/src/wire.ts:10](https://github.com/mertdogar/super-line/blob/130bd9adcf4898b57c097e3121e358d596527fa2/packages/core/src/wire.ts#L10) ## Properties ### d > **d**: `unknown` Defined in: [packages/core/src/wire.ts:14](https://github.com/mertdogar/super-line/blob/130bd9adcf4898b57c097e3121e358d596527fa2/packages/core/src/wire.ts#L14) *** ### i > **i**: `number` Defined in: [packages/core/src/wire.ts:12](https://github.com/mertdogar/super-line/blob/130bd9adcf4898b57c097e3121e358d596527fa2/packages/core/src/wire.ts#L12) *** ### m > **m**: `string` Defined in: [packages/core/src/wire.ts:13](https://github.com/mertdogar/super-line/blob/130bd9adcf4898b57c097e3121e358d596527fa2/packages/core/src/wire.ts#L13) *** ### t > **t**: `"req"` Defined in: [packages/core/src/wire.ts:11](https://github.com/mertdogar/super-line/blob/130bd9adcf4898b57c097e3121e358d596527fa2/packages/core/src/wire.ts#L11) --- --- url: >- https://super-line.dogar.biz/reference/@super-line/core/interfaces/RequestDef.md --- [Documentation](../../../index.md) / [@super-line/core](../index.md) / RequestDef # Interface: RequestDef Defined in: [packages/core/src/contract.ts:12](https://github.com/mertdogar/super-line/blob/130bd9adcf4898b57c097e3121e358d596527fa2/packages/core/src/contract.ts#L12) A client→server request (request/response). The client sends `input`; the server validates it, runs the handler, and replies with `output`. Fire-and-forget signals (no `output`) are not supported yet. ## Properties ### input > **input**: [`Schema`](../type-aliases/Schema.md) Defined in: [packages/core/src/contract.ts:14](https://github.com/mertdogar/super-line/blob/130bd9adcf4898b57c097e3121e358d596527fa2/packages/core/src/contract.ts#L14) Schema for the request payload the client sends. *** ### output > **output**: [`Schema`](../type-aliases/Schema.md) Defined in: [packages/core/src/contract.ts:16](https://github.com/mertdogar/super-line/blob/130bd9adcf4898b57c097e3121e358d596527fa2/packages/core/src/contract.ts#L16) Schema for the reply the server returns. --- --- url: 'https://super-line.dogar.biz/reference/@super-line/core/interfaces/ResFrame.md' --- [Documentation](../../../index.md) / [@super-line/core](../index.md) / ResFrame # Interface: ResFrame Defined in: [packages/core/src/wire.ts:87](https://github.com/mertdogar/super-line/blob/130bd9adcf4898b57c097e3121e358d596527fa2/packages/core/src/wire.ts#L87) ## Properties ### d > **d**: `unknown` Defined in: [packages/core/src/wire.ts:90](https://github.com/mertdogar/super-line/blob/130bd9adcf4898b57c097e3121e358d596527fa2/packages/core/src/wire.ts#L90) *** ### i > **i**: `number` Defined in: [packages/core/src/wire.ts:89](https://github.com/mertdogar/super-line/blob/130bd9adcf4898b57c097e3121e358d596527fa2/packages/core/src/wire.ts#L89) *** ### t > **t**: `"res"` Defined in: [packages/core/src/wire.ts:88](https://github.com/mertdogar/super-line/blob/130bd9adcf4898b57c097e3121e358d596527fa2/packages/core/src/wire.ts#L88) --- --- url: 'https://super-line.dogar.biz/reference/@super-line/core/interfaces/Resource.md' --- [Documentation](../../../index.md) / [@super-line/core](../index.md) / Resource # Interface: Resource\ Defined in: [packages/core/src/store.ts:27](https://github.com/mertdogar/super-line/blob/130bd9adcf4898b57c097e3121e358d596527fa2/packages/core/src/store.ts#L27) The unit a Store persists. `data` is opaque to core (a CRDT doc for a merging store, plain JSON for LWW). ## Type Parameters ### T `T` = `unknown` ## Properties ### accessRules > **accessRules**: [`AccessRules`](../type-aliases/AccessRules.md) Defined in: [packages/core/src/store.ts:29](https://github.com/mertdogar/super-line/blob/130bd9adcf4898b57c097e3121e358d596527fa2/packages/core/src/store.ts#L29) *** ### data > **data**: `T` Defined in: [packages/core/src/store.ts:30](https://github.com/mertdogar/super-line/blob/130bd9adcf4898b57c097e3121e358d596527fa2/packages/core/src/store.ts#L30) *** ### id > **id**: `string` Defined in: [packages/core/src/store.ts:28](https://github.com/mertdogar/super-line/blob/130bd9adcf4898b57c097e3121e358d596527fa2/packages/core/src/store.ts#L28) --- --- url: >- https://super-line.dogar.biz/reference/@super-line/core/interfaces/ResourceReplica.md --- [Documentation](../../../index.md) / [@super-line/core](../index.md) / ResourceReplica # Interface: ResourceReplica Defined in: [packages/core/src/store.ts:94](https://github.com/mertdogar/super-line/blob/130bd9adcf4898b57c097e3121e358d596527fa2/packages/core/src/store.ts#L94) A reactive handle over one opened Resource (mirrors super-store's `StoreValue` surface). `set`/`update` return the [StoreChange](StoreChange.md) to send up (null on a no-op); `applyRemote` merges an inbound Change (own-origin merges are idempotent / no-ops); `seed` hydrates the catch-up snapshot. ## Methods ### applyRemote() > **applyRemote**(`change`): `void` Defined in: [packages/core/src/store.ts:99](https://github.com/mertdogar/super-line/blob/130bd9adcf4898b57c097e3121e358d596527fa2/packages/core/src/store.ts#L99) #### Parameters ##### change [`StoreChange`](StoreChange.md) #### Returns `void` *** ### getSnapshot() > **getSnapshot**(): `unknown` Defined in: [packages/core/src/store.ts:95](https://github.com/mertdogar/super-line/blob/130bd9adcf4898b57c097e3121e358d596527fa2/packages/core/src/store.ts#L95) #### Returns `unknown` *** ### seed() > **seed**(`snapshot`): `void` Defined in: [packages/core/src/store.ts:100](https://github.com/mertdogar/super-line/blob/130bd9adcf4898b57c097e3121e358d596527fa2/packages/core/src/store.ts#L100) #### Parameters ##### snapshot `unknown` #### Returns `void` *** ### set() > **set**(`data`): [`StoreChange`](StoreChange.md) | `null` Defined in: [packages/core/src/store.ts:97](https://github.com/mertdogar/super-line/blob/130bd9adcf4898b57c097e3121e358d596527fa2/packages/core/src/store.ts#L97) #### Parameters ##### data `unknown` #### Returns [`StoreChange`](StoreChange.md) | `null` *** ### subscribe() > **subscribe**(`cb`): () => `void` Defined in: [packages/core/src/store.ts:96](https://github.com/mertdogar/super-line/blob/130bd9adcf4898b57c097e3121e358d596527fa2/packages/core/src/store.ts#L96) #### Parameters ##### cb () => `void` #### Returns () => `void` *** ### update() > **update**(`partial`): [`StoreChange`](StoreChange.md) | `null` Defined in: [packages/core/src/store.ts:98](https://github.com/mertdogar/super-line/blob/130bd9adcf4898b57c097e3121e358d596527fa2/packages/core/src/store.ts#L98) #### Parameters ##### partial `unknown` #### Returns [`StoreChange`](StoreChange.md) | `null` --- --- url: >- https://super-line.dogar.biz/reference/@super-line/core/interfaces/RoleBlock.md --- [Documentation](../../../index.md) / [@super-line/core](../index.md) / RoleBlock # Interface: RoleBlock Defined in: [packages/core/src/contract.ts:54](https://github.com/mertdogar/super-line/blob/130bd9adcf4898b57c097e3121e358d596527fa2/packages/core/src/contract.ts#L54) A role block: its directions plus an optional `data` schema typing `conn.data`. ## Extends * [`Directional`](Directional.md) ## Properties ### clientToServer? > `optional` **clientToServer?**: `Record`<`string`, [`RequestDef`](RequestDef.md)> Defined in: [packages/core/src/contract.ts:48](https://github.com/mertdogar/super-line/blob/130bd9adcf4898b57c097e3121e358d596527fa2/packages/core/src/contract.ts#L48) Requests this side may call (client→server). #### Inherited from [`Directional`](Directional.md).[`clientToServer`](Directional.md#clienttoserver) *** ### data? > `optional` **data?**: [`Schema`](../type-aliases/Schema.md) Defined in: [packages/core/src/contract.ts:56](https://github.com/mertdogar/super-line/blob/130bd9adcf4898b57c097e3121e358d596527fa2/packages/core/src/contract.ts#L56) Schema for this role's mutable per-connection `conn.data` (server-side scratch state). *** ### serverToClient? > `optional` **serverToClient?**: `Record`<`string`, [`ServerEntry`](../type-aliases/ServerEntry.md)> Defined in: [packages/core/src/contract.ts:50](https://github.com/mertdogar/super-line/blob/130bd9adcf4898b57c097e3121e358d596527fa2/packages/core/src/contract.ts#L50) Events, topics, and server→client requests this side may receive. #### Inherited from [`Directional`](Directional.md).[`serverToClient`](Directional.md#servertoclient) --- --- url: >- https://super-line.dogar.biz/reference/@super-line/core/interfaces/SChangeFrame.md --- [Documentation](../../../index.md) / [@super-line/core](../index.md) / SChangeFrame # Interface: SChangeFrame Defined in: [packages/core/src/wire.ts:118](https://github.com/mertdogar/super-line/blob/130bd9adcf4898b57c097e3121e358d596527fa2/packages/core/src/wire.ts#L118) ## Properties ### id > **id**: `string` Defined in: [packages/core/src/wire.ts:121](https://github.com/mertdogar/super-line/blob/130bd9adcf4898b57c097e3121e358d596527fa2/packages/core/src/wire.ts#L121) *** ### n > **n**: `string` Defined in: [packages/core/src/wire.ts:120](https://github.com/mertdogar/super-line/blob/130bd9adcf4898b57c097e3121e358d596527fa2/packages/core/src/wire.ts#L120) *** ### nd? > `optional` **nd?**: `string` Defined in: [packages/core/src/wire.ts:124](https://github.com/mertdogar/super-line/blob/130bd9adcf4898b57c097e3121e358d596527fa2/packages/core/src/wire.ts#L124) *** ### o > **o**: `string` Defined in: [packages/core/src/wire.ts:123](https://github.com/mertdogar/super-line/blob/130bd9adcf4898b57c097e3121e358d596527fa2/packages/core/src/wire.ts#L123) *** ### t > **t**: `"sch"` Defined in: [packages/core/src/wire.ts:119](https://github.com/mertdogar/super-line/blob/130bd9adcf4898b57c097e3121e358d596527fa2/packages/core/src/wire.ts#L119) *** ### u > **u**: `unknown` Defined in: [packages/core/src/wire.ts:122](https://github.com/mertdogar/super-line/blob/130bd9adcf4898b57c097e3121e358d596527fa2/packages/core/src/wire.ts#L122) --- --- url: >- https://super-line.dogar.biz/reference/@super-line/core/interfaces/SCloseFrame.md --- [Documentation](../../../index.md) / [@super-line/core](../index.md) / SCloseFrame # Interface: SCloseFrame Defined in: [packages/core/src/wire.ts:54](https://github.com/mertdogar/super-line/blob/130bd9adcf4898b57c097e3121e358d596527fa2/packages/core/src/wire.ts#L54) ## Properties ### id > **id**: `string` Defined in: [packages/core/src/wire.ts:57](https://github.com/mertdogar/super-line/blob/130bd9adcf4898b57c097e3121e358d596527fa2/packages/core/src/wire.ts#L57) *** ### n > **n**: `string` Defined in: [packages/core/src/wire.ts:56](https://github.com/mertdogar/super-line/blob/130bd9adcf4898b57c097e3121e358d596527fa2/packages/core/src/wire.ts#L56) *** ### t > **t**: `"sclose"` Defined in: [packages/core/src/wire.ts:55](https://github.com/mertdogar/super-line/blob/130bd9adcf4898b57c097e3121e358d596527fa2/packages/core/src/wire.ts#L55) --- --- url: >- https://super-line.dogar.biz/reference/@super-line/core/interfaces/Serializer.md --- [Documentation](../../../index.md) / [@super-line/core](../index.md) / Serializer # Interface: Serializer Defined in: [packages/core/src/serializer.ts:5](https://github.com/mertdogar/super-line/blob/130bd9adcf4898b57c097e3121e358d596527fa2/packages/core/src/serializer.ts#L5) Wire encoder/decoder. The server and client **must** use the same one. Swap in `superjson`/msgpack to carry richer types than JSON. ## Methods ### decode() > **decode**(`data`): `unknown` Defined in: [packages/core/src/serializer.ts:9](https://github.com/mertdogar/super-line/blob/130bd9adcf4898b57c097e3121e358d596527fa2/packages/core/src/serializer.ts#L9) Decode a wire frame back to a value. #### Parameters ##### data `string` | `Uint8Array`<`ArrayBufferLike`> #### Returns `unknown` *** ### encode() > **encode**(`value`): `string` | `Uint8Array`<`ArrayBufferLike`> Defined in: [packages/core/src/serializer.ts:7](https://github.com/mertdogar/super-line/blob/130bd9adcf4898b57c097e3121e358d596527fa2/packages/core/src/serializer.ts#L7) Encode a frame for the wire. #### Parameters ##### value `unknown` #### Returns `string` | `Uint8Array`<`ArrayBufferLike`> --- --- url: >- https://super-line.dogar.biz/reference/@super-line/core/interfaces/SErrFrame.md --- [Documentation](../../../index.md) / [@super-line/core](../index.md) / SErrFrame # Interface: SErrFrame Defined in: [packages/core/src/wire.ts:31](https://github.com/mertdogar/super-line/blob/130bd9adcf4898b57c097e3121e358d596527fa2/packages/core/src/wire.ts#L31) ## Properties ### code > **code**: `string` Defined in: [packages/core/src/wire.ts:34](https://github.com/mertdogar/super-line/blob/130bd9adcf4898b57c097e3121e358d596527fa2/packages/core/src/wire.ts#L34) *** ### d? > `optional` **d?**: `unknown` Defined in: [packages/core/src/wire.ts:36](https://github.com/mertdogar/super-line/blob/130bd9adcf4898b57c097e3121e358d596527fa2/packages/core/src/wire.ts#L36) *** ### i > **i**: `number` Defined in: [packages/core/src/wire.ts:33](https://github.com/mertdogar/super-line/blob/130bd9adcf4898b57c097e3121e358d596527fa2/packages/core/src/wire.ts#L33) *** ### m > **m**: `string` Defined in: [packages/core/src/wire.ts:35](https://github.com/mertdogar/super-line/blob/130bd9adcf4898b57c097e3121e358d596527fa2/packages/core/src/wire.ts#L35) *** ### t > **t**: `"serr"` Defined in: [packages/core/src/wire.ts:32](https://github.com/mertdogar/super-line/blob/130bd9adcf4898b57c097e3121e358d596527fa2/packages/core/src/wire.ts#L32) --- --- url: >- https://super-line.dogar.biz/reference/@super-line/core/interfaces/ServerMessageDef.md --- [Documentation](../../../index.md) / [@super-line/core](../index.md) / ServerMessageDef # Interface: ServerMessageDef Defined in: [packages/core/src/contract.ts:23](https://github.com/mertdogar/super-line/blob/130bd9adcf4898b57c097e3121e358d596527fa2/packages/core/src/contract.ts#L23) A server→client message. With `subscribe: true` it becomes a client-subscribable **topic**; otherwise it is a server-pushed **event**. ## Properties ### payload > **payload**: [`Schema`](../type-aliases/Schema.md) Defined in: [packages/core/src/contract.ts:25](https://github.com/mertdogar/super-line/blob/130bd9adcf4898b57c097e3121e358d596527fa2/packages/core/src/contract.ts#L25) Schema for the message body. *** ### subscribe? > `optional` **subscribe?**: `boolean` Defined in: [packages/core/src/contract.ts:27](https://github.com/mertdogar/super-line/blob/130bd9adcf4898b57c097e3121e358d596527fa2/packages/core/src/contract.ts#L27) When `true`, clients opt in via `client.subscribe(...)` (a topic). Omit for a push event. --- --- url: >- https://super-line.dogar.biz/reference/@super-line/core/interfaces/ServerRequestDef.md --- [Documentation](../../../index.md) / [@super-line/core](../index.md) / ServerRequestDef # Interface: ServerRequestDef Defined in: [packages/core/src/contract.ts:35](https://github.com/mertdogar/super-line/blob/130bd9adcf4898b57c097e3121e358d596527fa2/packages/core/src/contract.ts#L35) A server→client request (request/response). The server sends `input`; the client's `implement` handler returns `output`. Lives in `serverToClient` alongside events and topics, distinguished by having `input`. ## Properties ### input > **input**: [`Schema`](../type-aliases/Schema.md) Defined in: [packages/core/src/contract.ts:37](https://github.com/mertdogar/super-line/blob/130bd9adcf4898b57c097e3121e358d596527fa2/packages/core/src/contract.ts#L37) Schema for the request payload the server sends. *** ### output > **output**: [`Schema`](../type-aliases/Schema.md) Defined in: [packages/core/src/contract.ts:39](https://github.com/mertdogar/super-line/blob/130bd9adcf4898b57c097e3121e358d596527fa2/packages/core/src/contract.ts#L39) Schema for the reply the client returns. --- --- url: >- https://super-line.dogar.biz/reference/@super-line/core/interfaces/ServerStore.md --- [Documentation](../../../index.md) / [@super-line/core](../index.md) / ServerStore # Interface: ServerStore Defined in: [packages/core/src/store.ts:52](https://github.com/mertdogar/super-line/blob/130bd9adcf4898b57c097e3121e358d596527fa2/packages/core/src/store.ts#L52) The server half of a Store pair: persistence + the consistency model + change-notification. It does NOT enforce access (core does) and does NOT touch the wire. `apply` interprets a [StoreChange](StoreChange.md) per its consistency model (LWW replace vs CRDT merge); every applied mutation — client write, server co-write, or relayed remote change — must surface through [ServerStore.onChange](#onchange), which is core's single fan-out source. ## Properties ### clustering > `readonly` **clustering**: `"relay"` | `"self"` Defined in: [packages/core/src/store.ts:57](https://github.com/mertdogar/super-line/blob/130bd9adcf4898b57c097e3121e358d596527fa2/packages/core/src/store.ts#L57) How cross-node sync happens: `relay` (core relays Changes over the adapter; each node a replica) or `self` (the store owns a shared backend and core fans only to local subscribers). ## Methods ### apply() > **apply**(`change`): `Awaitable`<`void`> Defined in: [packages/core/src/store.ts:63](https://github.com/mertdogar/super-line/blob/130bd9adcf4898b57c097e3121e358d596527fa2/packages/core/src/store.ts#L63) Apply an inbound Change — replace (LWW) or merge (CRDT), the store's choice. #### Parameters ##### change [`StoreChange`](StoreChange.md) #### Returns `Awaitable`<`void`> *** ### close()? > `optional` **close**(): `Awaitable`<`void`> Defined in: [packages/core/src/store.ts:73](https://github.com/mertdogar/super-line/blob/130bd9adcf4898b57c097e3121e358d596527fa2/packages/core/src/store.ts#L73) Release any resources held by the store. #### Returns `Awaitable`<`void`> *** ### create() > **create**(`id`, `data`, `accessRules`): `Awaitable`<`void`> Defined in: [packages/core/src/store.ts:61](https://github.com/mertdogar/super-line/blob/130bd9adcf4898b57c097e3121e358d596527fa2/packages/core/src/store.ts#L61) Create a Resource with initial data + access rules (server-authoritative). #### Parameters ##### id `string` ##### data `unknown` ##### accessRules [`AccessRules`](../type-aliases/AccessRules.md) #### Returns `Awaitable`<`void`> *** ### delete() > **delete**(`id`): `Awaitable`<`void`> Defined in: [packages/core/src/store.ts:67](https://github.com/mertdogar/super-line/blob/130bd9adcf4898b57c097e3121e358d596527fa2/packages/core/src/store.ts#L67) Remove a Resource. #### Parameters ##### id `string` #### Returns `Awaitable`<`void`> *** ### list() > **list**(): `Awaitable`<`string`\[]> Defined in: [packages/core/src/store.ts:69](https://github.com/mertdogar/super-line/blob/130bd9adcf4898b57c097e3121e358d596527fa2/packages/core/src/store.ts#L69) All Resource ids in this store (core ACL-filters before returning ids to a client). #### Returns `Awaitable`<`string`\[]> *** ### onChange() > **onChange**(`cb`): () => `void` Defined in: [packages/core/src/store.ts:71](https://github.com/mertdogar/super-line/blob/130bd9adcf4898b57c097e3121e358d596527fa2/packages/core/src/store.ts#L71) Subscribe to every applied mutation — the single fan-out source. Returns an unsubscribe fn. #### Parameters ##### cb (`change`) => `void` #### Returns () => `void` *** ### read() > **read**(`id`): `Awaitable`<[`Resource`](Resource.md)<`unknown`> | `undefined`> Defined in: [packages/core/src/store.ts:59](https://github.com/mertdogar/super-line/blob/130bd9adcf4898b57c097e3121e358d596527fa2/packages/core/src/store.ts#L59) Current snapshot of a Resource (for catch-up on subscribe), or undefined if absent. #### Parameters ##### id `string` #### Returns `Awaitable`<[`Resource`](Resource.md)<`unknown`> | `undefined`> *** ### setAccess() > **setAccess**(`id`, `accessRules`): `Awaitable`<`void`> Defined in: [packages/core/src/store.ts:65](https://github.com/mertdogar/super-line/blob/130bd9adcf4898b57c097e3121e358d596527fa2/packages/core/src/store.ts#L65) Replace a Resource's access rules. #### Parameters ##### id `string` ##### accessRules [`AccessRules`](../type-aliases/AccessRules.md) #### Returns `Awaitable`<`void`> --- --- url: >- https://super-line.dogar.biz/reference/@super-line/core/interfaces/ServerTransport.md --- [Documentation](../../../index.md) / [@super-line/core](../index.md) / ServerTransport # Interface: ServerTransport Defined in: [packages/core/src/transport.ts:56](https://github.com/mertdogar/super-line/blob/130bd9adcf4898b57c097e3121e358d596527fa2/packages/core/src/transport.ts#L56) Server side: the transport listens, authenticates each inbound connection at its native moment, and surfaces only the accepted ones — so the core never holds an unauthenticated connection. ## Methods ### start() > **start**(`hooks`): `void` | `Promise`<`void`> Defined in: [packages/core/src/transport.ts:57](https://github.com/mertdogar/super-line/blob/130bd9adcf4898b57c097e3121e358d596527fa2/packages/core/src/transport.ts#L57) #### Parameters ##### hooks ###### authenticate (`h`) => `Promise`<[`AuthOutcome`](../type-aliases/AuthOutcome.md)> Core owns the decision; the transport calls this at its native auth point and rejects natively on throw. ###### onConnection (`raw`, `auth`) => `void` Fires ONLY for accepted connections. #### Returns `void` | `Promise`<`void`> *** ### stop() > **stop**(): `void` | `Promise`<`void`> Defined in: [packages/core/src/transport.ts:64](https://github.com/mertdogar/super-line/blob/130bd9adcf4898b57c097e3121e358d596527fa2/packages/core/src/transport.ts#L64) Stop listening and drop in-flight connections. #### Returns `void` | `Promise`<`void`> --- --- url: >- https://super-line.dogar.biz/reference/@super-line/core/interfaces/SOpenFrame.md --- [Documentation](../../../index.md) / [@super-line/core](../index.md) / SOpenFrame # Interface: SOpenFrame Defined in: [packages/core/src/wire.ts:48](https://github.com/mertdogar/super-line/blob/130bd9adcf4898b57c097e3121e358d596527fa2/packages/core/src/wire.ts#L48) ## Properties ### i > **i**: `number` Defined in: [packages/core/src/wire.ts:50](https://github.com/mertdogar/super-line/blob/130bd9adcf4898b57c097e3121e358d596527fa2/packages/core/src/wire.ts#L50) *** ### id > **id**: `string` Defined in: [packages/core/src/wire.ts:52](https://github.com/mertdogar/super-line/blob/130bd9adcf4898b57c097e3121e358d596527fa2/packages/core/src/wire.ts#L52) *** ### n > **n**: `string` Defined in: [packages/core/src/wire.ts:51](https://github.com/mertdogar/super-line/blob/130bd9adcf4898b57c097e3121e358d596527fa2/packages/core/src/wire.ts#L51) *** ### t > **t**: `"sopen"` Defined in: [packages/core/src/wire.ts:49](https://github.com/mertdogar/super-line/blob/130bd9adcf4898b57c097e3121e358d596527fa2/packages/core/src/wire.ts#L49) --- --- url: >- https://super-line.dogar.biz/reference/@super-line/core/interfaces/SReadFrame.md --- [Documentation](../../../index.md) / [@super-line/core](../index.md) / SReadFrame # Interface: SReadFrame Defined in: [packages/core/src/wire.ts:67](https://github.com/mertdogar/super-line/blob/130bd9adcf4898b57c097e3121e358d596527fa2/packages/core/src/wire.ts#L67) ## Properties ### i > **i**: `number` Defined in: [packages/core/src/wire.ts:69](https://github.com/mertdogar/super-line/blob/130bd9adcf4898b57c097e3121e358d596527fa2/packages/core/src/wire.ts#L69) *** ### id > **id**: `string` Defined in: [packages/core/src/wire.ts:71](https://github.com/mertdogar/super-line/blob/130bd9adcf4898b57c097e3121e358d596527fa2/packages/core/src/wire.ts#L71) *** ### n > **n**: `string` Defined in: [packages/core/src/wire.ts:70](https://github.com/mertdogar/super-line/blob/130bd9adcf4898b57c097e3121e358d596527fa2/packages/core/src/wire.ts#L70) *** ### t > **t**: `"srd"` Defined in: [packages/core/src/wire.ts:68](https://github.com/mertdogar/super-line/blob/130bd9adcf4898b57c097e3121e358d596527fa2/packages/core/src/wire.ts#L68) --- --- url: >- https://super-line.dogar.biz/reference/@super-line/core/interfaces/SReqFrame.md --- [Documentation](../../../index.md) / [@super-line/core](../index.md) / SReqFrame # Interface: SReqFrame Defined in: [packages/core/src/wire.ts:111](https://github.com/mertdogar/super-line/blob/130bd9adcf4898b57c097e3121e358d596527fa2/packages/core/src/wire.ts#L111) ## Properties ### d > **d**: `unknown` Defined in: [packages/core/src/wire.ts:115](https://github.com/mertdogar/super-line/blob/130bd9adcf4898b57c097e3121e358d596527fa2/packages/core/src/wire.ts#L115) *** ### i > **i**: `number` Defined in: [packages/core/src/wire.ts:113](https://github.com/mertdogar/super-line/blob/130bd9adcf4898b57c097e3121e358d596527fa2/packages/core/src/wire.ts#L113) *** ### m > **m**: `string` Defined in: [packages/core/src/wire.ts:114](https://github.com/mertdogar/super-line/blob/130bd9adcf4898b57c097e3121e358d596527fa2/packages/core/src/wire.ts#L114) *** ### t > **t**: `"sreq"` Defined in: [packages/core/src/wire.ts:112](https://github.com/mertdogar/super-line/blob/130bd9adcf4898b57c097e3121e358d596527fa2/packages/core/src/wire.ts#L112) --- --- url: >- https://super-line.dogar.biz/reference/@super-line/core/interfaces/SResFrame.md --- [Documentation](../../../index.md) / [@super-line/core](../index.md) / SResFrame # Interface: SResFrame Defined in: [packages/core/src/wire.ts:26](https://github.com/mertdogar/super-line/blob/130bd9adcf4898b57c097e3121e358d596527fa2/packages/core/src/wire.ts#L26) ## Properties ### d > **d**: `unknown` Defined in: [packages/core/src/wire.ts:29](https://github.com/mertdogar/super-line/blob/130bd9adcf4898b57c097e3121e358d596527fa2/packages/core/src/wire.ts#L29) *** ### i > **i**: `number` Defined in: [packages/core/src/wire.ts:28](https://github.com/mertdogar/super-line/blob/130bd9adcf4898b57c097e3121e358d596527fa2/packages/core/src/wire.ts#L28) *** ### t > **t**: `"sres"` Defined in: [packages/core/src/wire.ts:27](https://github.com/mertdogar/super-line/blob/130bd9adcf4898b57c097e3121e358d596527fa2/packages/core/src/wire.ts#L27) --- --- url: >- https://super-line.dogar.biz/reference/@super-line/core/interfaces/StoreChange.md --- [Documentation](../../../index.md) / [@super-line/core](../index.md) / StoreChange # Interface: StoreChange Defined in: [packages/core/src/store.ts:39](https://github.com/mertdogar/super-line/blob/130bd9adcf4898b57c097e3121e358d596527fa2/packages/core/src/store.ts#L39) What a Store emits when a Resource mutates — and the symmetric shape a write carries IN. `update` is a store-DEFINED opaque payload (a CRDT delta, or a full JSON value for last-writer-wins); core relays it without parsing (base64 it if it's bytes under the JSON serializer). `origin` is the per-writer id used for echo-break — never the [Principal](../type-aliases/Principal.md), never the CRDT actor id. ## Properties ### id > **id**: `string` Defined in: [packages/core/src/store.ts:40](https://github.com/mertdogar/super-line/blob/130bd9adcf4898b57c097e3121e358d596527fa2/packages/core/src/store.ts#L40) *** ### origin > **origin**: `string` Defined in: [packages/core/src/store.ts:42](https://github.com/mertdogar/super-line/blob/130bd9adcf4898b57c097e3121e358d596527fa2/packages/core/src/store.ts#L42) *** ### update > **update**: `unknown` Defined in: [packages/core/src/store.ts:41](https://github.com/mertdogar/super-line/blob/130bd9adcf4898b57c097e3121e358d596527fa2/packages/core/src/store.ts#L41) --- --- url: 'https://super-line.dogar.biz/reference/@super-line/core/interfaces/SubFrame.md' --- [Documentation](../../../index.md) / [@super-line/core](../index.md) / SubFrame # Interface: SubFrame Defined in: [packages/core/src/wire.ts:16](https://github.com/mertdogar/super-line/blob/130bd9adcf4898b57c097e3121e358d596527fa2/packages/core/src/wire.ts#L16) ## Properties ### c > **c**: `string` Defined in: [packages/core/src/wire.ts:19](https://github.com/mertdogar/super-line/blob/130bd9adcf4898b57c097e3121e358d596527fa2/packages/core/src/wire.ts#L19) *** ### i > **i**: `number` Defined in: [packages/core/src/wire.ts:18](https://github.com/mertdogar/super-line/blob/130bd9adcf4898b57c097e3121e358d596527fa2/packages/core/src/wire.ts#L18) *** ### t > **t**: `"sub"` Defined in: [packages/core/src/wire.ts:17](https://github.com/mertdogar/super-line/blob/130bd9adcf4898b57c097e3121e358d596527fa2/packages/core/src/wire.ts#L17) --- --- url: >- https://super-line.dogar.biz/reference/@super-line/core/interfaces/SWriteFrame.md --- [Documentation](../../../index.md) / [@super-line/core](../index.md) / SWriteFrame # Interface: SWriteFrame Defined in: [packages/core/src/wire.ts:59](https://github.com/mertdogar/super-line/blob/130bd9adcf4898b57c097e3121e358d596527fa2/packages/core/src/wire.ts#L59) ## Properties ### i > **i**: `number` Defined in: [packages/core/src/wire.ts:61](https://github.com/mertdogar/super-line/blob/130bd9adcf4898b57c097e3121e358d596527fa2/packages/core/src/wire.ts#L61) *** ### id > **id**: `string` Defined in: [packages/core/src/wire.ts:63](https://github.com/mertdogar/super-line/blob/130bd9adcf4898b57c097e3121e358d596527fa2/packages/core/src/wire.ts#L63) *** ### n > **n**: `string` Defined in: [packages/core/src/wire.ts:62](https://github.com/mertdogar/super-line/blob/130bd9adcf4898b57c097e3121e358d596527fa2/packages/core/src/wire.ts#L62) *** ### o > **o**: `string` Defined in: [packages/core/src/wire.ts:65](https://github.com/mertdogar/super-line/blob/130bd9adcf4898b57c097e3121e358d596527fa2/packages/core/src/wire.ts#L65) *** ### t > **t**: `"swr"` Defined in: [packages/core/src/wire.ts:60](https://github.com/mertdogar/super-line/blob/130bd9adcf4898b57c097e3121e358d596527fa2/packages/core/src/wire.ts#L60) *** ### u > **u**: `unknown` Defined in: [packages/core/src/wire.ts:64](https://github.com/mertdogar/super-line/blob/130bd9adcf4898b57c097e3121e358d596527fa2/packages/core/src/wire.ts#L64) --- --- url: >- https://super-line.dogar.biz/reference/@super-line/core/interfaces/UnsubFrame.md --- [Documentation](../../../index.md) / [@super-line/core](../index.md) / UnsubFrame # Interface: UnsubFrame Defined in: [packages/core/src/wire.ts:21](https://github.com/mertdogar/super-line/blob/130bd9adcf4898b57c097e3121e358d596527fa2/packages/core/src/wire.ts#L21) ## Properties ### c > **c**: `string` Defined in: [packages/core/src/wire.ts:23](https://github.com/mertdogar/super-line/blob/130bd9adcf4898b57c097e3121e358d596527fa2/packages/core/src/wire.ts#L23) *** ### t > **t**: `"unsub"` Defined in: [packages/core/src/wire.ts:22](https://github.com/mertdogar/super-line/blob/130bd9adcf4898b57c097e3121e358d596527fa2/packages/core/src/wire.ts#L22) --- --- url: >- https://super-line.dogar.biz/reference/@super-line/core/type-aliases/AccessRules.md --- [Documentation](../../../index.md) / [@super-line/core](../index.md) / AccessRules # Type Alias: AccessRules > **AccessRules** = `Record`<[`Principal`](Principal.md), [`Perms`](../interfaces/Perms.md)> Defined in: [packages/core/src/store.ts:24](https://github.com/mertdogar/super-line/blob/130bd9adcf4898b57c097e3121e358d596527fa2/packages/core/src/store.ts#L24) A Resource's access map: which [Principal](Principal.md) may read/write. Server-authoritative, deny-by-default. --- --- url: >- https://super-line.dogar.biz/reference/@super-line/core/type-aliases/AnyData.md --- [Documentation](../../../index.md) / [@super-line/core](../index.md) / AnyData # Type Alias: AnyData\ > **AnyData**<`C`> = [`DataOf`](DataOf.md)<`C`, [`RoleOf`](RoleOf.md)<`C`>> Defined in: [packages/core/src/contract.ts:143](https://github.com/mertdogar/super-line/blob/130bd9adcf4898b57c097e3121e358d596527fa2/packages/core/src/contract.ts#L143) Union of every role's `conn.data` shape (used where the role isn't narrowed, e.g. shared handlers). ## Type Parameters ### C `C` *extends* [`Contract`](../interfaces/Contract.md) --- --- url: >- https://super-line.dogar.biz/reference/@super-line/core/type-aliases/AuthOutcome.md --- [Documentation](../../../index.md) / [@super-line/core](../index.md) / AuthOutcome # Type Alias: AuthOutcome > **AuthOutcome** = `object` Defined in: [packages/core/src/transport.ts:49](https://github.com/mertdogar/super-line/blob/130bd9adcf4898b57c097e3121e358d596527fa2/packages/core/src/transport.ts#L49) What `authenticate` returns. Reject by throwing — the transport then rejects in its native idiom. `transport` is injected by the server (from [Handshake.transport](../interfaces/Handshake.md#transport)); user `authenticate` callbacks return only `role` + `ctx`. ## Properties ### ctx > **ctx**: `unknown` Defined in: [packages/core/src/transport.ts:49](https://github.com/mertdogar/super-line/blob/130bd9adcf4898b57c097e3121e358d596527fa2/packages/core/src/transport.ts#L49) *** ### role > **role**: `string` Defined in: [packages/core/src/transport.ts:49](https://github.com/mertdogar/super-line/blob/130bd9adcf4898b57c097e3121e358d596527fa2/packages/core/src/transport.ts#L49) *** ### transport? > `optional` **transport?**: `string` Defined in: [packages/core/src/transport.ts:49](https://github.com/mertdogar/super-line/blob/130bd9adcf4898b57c097e3121e358d596527fa2/packages/core/src/transport.ts#L49) --- --- url: >- https://super-line.dogar.biz/reference/@super-line/core/type-aliases/ClientFrame.md --- [Documentation](../../../index.md) / [@super-line/core](../index.md) / ClientFrame # Type Alias: ClientFrame > **ClientFrame** = [`ReqFrame`](../interfaces/ReqFrame.md) | [`SubFrame`](../interfaces/SubFrame.md) | [`UnsubFrame`](../interfaces/UnsubFrame.md) | [`SResFrame`](../interfaces/SResFrame.md) | [`SErrFrame`](../interfaces/SErrFrame.md) | [`SOpenFrame`](../interfaces/SOpenFrame.md) | [`SCloseFrame`](../interfaces/SCloseFrame.md) | [`SWriteFrame`](../interfaces/SWriteFrame.md) | [`SReadFrame`](../interfaces/SReadFrame.md) | [`PingFrame`](../interfaces/PingFrame.md) | [`PongFrame`](../interfaces/PongFrame.md) Defined in: [packages/core/src/wire.ts:73](https://github.com/mertdogar/super-line/blob/130bd9adcf4898b57c097e3121e358d596527fa2/packages/core/src/wire.ts#L73) --- --- url: >- https://super-line.dogar.biz/reference/@super-line/core/type-aliases/ClientInput.md --- [Documentation](../../../index.md) / [@super-line/core](../index.md) / ClientInput # Type Alias: ClientInput\ > **ClientInput**<`T`> = `T` *extends* [`RequestDef`](../interfaces/RequestDef.md) ? [`InferIn`](InferIn.md)<`T`\[`"input"`]> : `never` Defined in: [packages/core/src/contract.ts:147](https://github.com/mertdogar/super-line/blob/130bd9adcf4898b57c097e3121e358d596527fa2/packages/core/src/contract.ts#L147) The input type a client passes for a request (pre-validation). ## Type Parameters ### T `T` --- --- url: 'https://super-line.dogar.biz/reference/@super-line/core/type-aliases/DataOf.md' --- [Documentation](../../../index.md) / [@super-line/core](../index.md) / DataOf # Type Alias: DataOf\ > **DataOf**<`C`, `R`> = `C`\[`"roles"`]\[`R`] *extends* `object` ? [`InferOut`](InferOut.md)<`S`> : `Record`<`string`, `never`> Defined in: [packages/core/src/contract.ts:137](https://github.com/mertdogar/super-line/blob/130bd9adcf4898b57c097e3121e358d596527fa2/packages/core/src/contract.ts#L137) The typed shape of `conn.data` for role `R` (its `data` schema, or an empty object). ## Type Parameters ### C `C` *extends* [`Contract`](../interfaces/Contract.md) ### R `R` *extends* [`RoleOf`](RoleOf.md)<`C`> --- --- url: >- https://super-line.dogar.biz/reference/@super-line/core/type-aliases/EmitData.md --- [Documentation](../../../index.md) / [@super-line/core](../index.md) / EmitData # Type Alias: EmitData\ > **EmitData**<`T`> = `T` *extends* [`ServerMessageDef`](../interfaces/ServerMessageDef.md) ? [`InferIn`](InferIn.md)<`T`\[`"payload"`]> : `never` Defined in: [packages/core/src/contract.ts:155](https://github.com/mertdogar/super-line/blob/130bd9adcf4898b57c097e3121e358d596527fa2/packages/core/src/contract.ts#L155) The data a server sends for an event/topic (pre-validation). ## Type Parameters ### T `T` --- --- url: >- https://super-line.dogar.biz/reference/@super-line/core/type-aliases/ErrorCode.md --- [Documentation](../../../index.md) / [@super-line/core](../index.md) / ErrorCode # Type Alias: ErrorCode > **ErrorCode** = [`SuperLineErrorCode`](SuperLineErrorCode.md) | `string` & `object` Defined in: [packages/core/src/errors.ts:13](https://github.com/mertdogar/super-line/blob/130bd9adcf4898b57c097e3121e358d596527fa2/packages/core/src/errors.ts#L13) A built-in code or any custom string (autocomplete keeps the known set). --- --- url: >- https://super-line.dogar.biz/reference/@super-line/core/type-aliases/EventData.md --- [Documentation](../../../index.md) / [@super-line/core](../index.md) / EventData # Type Alias: EventData\ > **EventData**<`T`> = `T` *extends* [`ServerMessageDef`](../interfaces/ServerMessageDef.md) ? [`InferOut`](InferOut.md)<`T`\[`"payload"`]> : `never` Defined in: [packages/core/src/contract.ts:153](https://github.com/mertdogar/super-line/blob/130bd9adcf4898b57c097e3121e358d596527fa2/packages/core/src/contract.ts#L153) The data a client receives for an event/topic (post-validation). ## Type Parameters ### T `T` --- --- url: 'https://super-line.dogar.biz/reference/@super-line/core/type-aliases/Events.md' --- [Documentation](../../../index.md) / [@super-line/core](../index.md) / Events # Type Alias: Events\ > **Events**<`C`, `R`> = `EventsOf`<[`ServerMessages`](ServerMessages.md)<`C`, `R`>> Defined in: [packages/core/src/contract.ts:116](https://github.com/mertdogar/super-line/blob/130bd9adcf4898b57c097e3121e358d596527fa2/packages/core/src/contract.ts#L116) A role's push events (server→client entries without `subscribe`). ## Type Parameters ### C `C` *extends* [`Contract`](../interfaces/Contract.md) ### R `R` *extends* [`RoleOf`](RoleOf.md)<`C`> --- --- url: 'https://super-line.dogar.biz/reference/@super-line/core/type-aliases/Frame.md' --- [Documentation](../../../index.md) / [@super-line/core](../index.md) / Frame # Type Alias: Frame > **Frame** = [`ClientFrame`](ClientFrame.md) | [`ServerFrame`](ServerFrame.md) Defined in: [packages/core/src/wire.ts:136](https://github.com/mertdogar/super-line/blob/130bd9adcf4898b57c097e3121e358d596527fa2/packages/core/src/wire.ts#L136) --- --- url: >- https://super-line.dogar.biz/reference/@super-line/core/type-aliases/InferIn.md --- [Documentation](../../../index.md) / [@super-line/core](../index.md) / InferIn # Type Alias: InferIn\ > **InferIn**<`S`> = `StandardSchemaV1.InferInput`<`S`> Defined in: [packages/core/src/contract.ts:158](https://github.com/mertdogar/super-line/blob/130bd9adcf4898b57c097e3121e358d596527fa2/packages/core/src/contract.ts#L158) Infer a schema's **input** type (what you pass into the validator). ## Type Parameters ### S `S` *extends* [`Schema`](Schema.md) --- --- url: >- https://super-line.dogar.biz/reference/@super-line/core/type-aliases/InferOut.md --- [Documentation](../../../index.md) / [@super-line/core](../index.md) / InferOut # Type Alias: InferOut\ > **InferOut**<`S`> = `StandardSchemaV1.InferOutput`<`S`> Defined in: [packages/core/src/contract.ts:160](https://github.com/mertdogar/super-line/blob/130bd9adcf4898b57c097e3121e358d596527fa2/packages/core/src/contract.ts#L160) Infer a schema's **output** type (the validated result). ## Type Parameters ### S `S` *extends* [`Schema`](Schema.md) --- --- url: >- https://super-line.dogar.biz/reference/@super-line/core/type-aliases/InspectorEvent.md --- [Documentation](../../../index.md) / [@super-line/core](../index.md) / InspectorEvent # Type Alias: InspectorEvent > **InspectorEvent** = { `descriptor`: [`ConnDescriptor`](../interfaces/ConnDescriptor.md); `type`: `"connect"`; } | { `connId`: `string`; `nodeId`: `string`; `type`: `"disconnect"`; `userId?`: `string`; } | { `connId`: `string`; `room`: `string`; `type`: `"room.add"`; } | { `connId`: `string`; `room`: `string`; `type`: `"room.remove"`; } | { `connId`: `string`; `topic`: `string`; `type`: `"topic.sub"`; } | { `connId`: `string`; `topic`: `string`; `type`: `"topic.unsub"`; } | { `connId`: `string`; `input`: `unknown`; `name`: `string`; `role`: `string`; `type`: `"msg.request"`; } | { `connId`: `string`; `error?`: `MessageError`; `name`: `string`; `ok`: `boolean`; `output?`: `unknown`; `type`: `"msg.response"`; } | { `data`: `unknown`; `name`: `string`; `target`: `string`; `type`: `"msg.event"`; } | { `data`: `unknown`; `name`: `string`; `room`: `string`; `type`: `"msg.broadcast"`; } | { `data`: `unknown`; `topic`: `string`; `type`: `"msg.publish"`; } | { `input`: `unknown`; `name`: `string`; `target`: `string`; `type`: `"msg.serverRequest"`; } | { `error?`: `MessageError`; `name`: `string`; `ok`: `boolean`; `output?`: `unknown`; `target`: `string`; `type`: `"msg.serverReply"`; } | { `connId?`: `string`; `data`: `unknown`; `id`: `string`; `origin`: `string`; `store`: `string`; `type`: `"store.write"`; } | { `id`: `string`; `perms`: { `read`: `boolean`; `write`: `boolean`; }; `principal`: `string`; `store`: `string`; `type`: `"store.grant"`; } | { `id`: `string`; `principal`: `string`; `store`: `string`; `type`: `"store.revoke"`; } | { `connId`: `string`; `id`: `string`; `store`: `string`; `type`: `"store.subscribe"`; } | { `connId`: `string`; `id`: `string`; `store`: `string`; `type`: `"store.unsubscribe"`; } Defined in: [packages/core/src/inspector.ts:72](https://github.com/mertdogar/super-line/blob/130bd9adcf4898b57c097e3121e358d596527fa2/packages/core/src/inspector.ts#L72) A live event pushed on the `events` topic, fanned out cluster-wide. Lifecycle events (connect/disconnect/room/topic) are always emitted when inspector is on; `msg.*` events carry actual message traffic and are only emitted when inspector is on. Message payloads are safe-serialized and field-redacted (via the `inspector.redact` list) before they cross the bus. --- --- url: >- https://super-line.dogar.biz/reference/@super-line/core/type-aliases/MessageFlavor.md --- [Documentation](../../../index.md) / [@super-line/core](../index.md) / MessageFlavor # Type Alias: MessageFlavor > **MessageFlavor** = `"request"` | `"event"` | `"topic"` | `"serverRequest"` Defined in: [packages/core/src/inspector.ts:13](https://github.com/mertdogar/super-line/blob/130bd9adcf4898b57c097e3121e358d596527fa2/packages/core/src/inspector.ts#L13) How a contract message is used on the wire. --- --- url: 'https://super-line.dogar.biz/reference/@super-line/core/type-aliases/Output.md' --- [Documentation](../../../index.md) / [@super-line/core](../index.md) / Output # Type Alias: Output\ > **Output**<`T`> = `T` *extends* [`RequestDef`](../interfaces/RequestDef.md) ? [`InferOut`](InferOut.md)<`T`\[`"output"`]> : `never` Defined in: [packages/core/src/contract.ts:151](https://github.com/mertdogar/super-line/blob/130bd9adcf4898b57c097e3121e358d596527fa2/packages/core/src/contract.ts#L151) The reply type of a request (server returns / client receives). ## Type Parameters ### T `T` --- --- url: >- https://super-line.dogar.biz/reference/@super-line/core/type-aliases/Principal.md --- [Documentation](../../../index.md) / [@super-line/core](../index.md) / Principal # Type Alias: Principal > **Principal** = `string` Defined in: [packages/core/src/store.ts:15](https://github.com/mertdogar/super-line/blob/130bd9adcf4898b57c097e3121e358d596527fa2/packages/core/src/store.ts#L15) The ACL identity a Resource's access is keyed by (`identify(conn) ?? conn.id`). --- --- url: >- https://super-line.dogar.biz/reference/@super-line/core/type-aliases/Requests.md --- [Documentation](../../../index.md) / [@super-line/core](../index.md) / Requests # Type Alias: Requests\ > **Requests**<`C`, `R`> = `CtsOf`<`C`\[`"shared"`]> & `CtsOf`<`C`\[`"roles"`]\[`R`]> Defined in: [packages/core/src/contract.ts:110](https://github.com/mertdogar/super-line/blob/130bd9adcf4898b57c097e3121e358d596527fa2/packages/core/src/contract.ts#L110) A role's effective request map: `shared` ∪ `roles[R]` client→server requests. ## Type Parameters ### C `C` *extends* [`Contract`](../interfaces/Contract.md) ### R `R` *extends* [`RoleOf`](RoleOf.md)<`C`> --- --- url: 'https://super-line.dogar.biz/reference/@super-line/core/type-aliases/RoleOf.md' --- [Documentation](../../../index.md) / [@super-line/core](../index.md) / RoleOf # Type Alias: RoleOf\ > **RoleOf**<`C`> = keyof `C`\[`"roles"`] & `string` Defined in: [packages/core/src/contract.ts:97](https://github.com/mertdogar/super-line/blob/130bd9adcf4898b57c097e3121e358d596527fa2/packages/core/src/contract.ts#L97) Union of a contract's role names. ## Type Parameters ### C `C` *extends* [`Contract`](../interfaces/Contract.md) --- --- url: >- https://super-line.dogar.biz/reference/@super-line/core/type-aliases/RoleRequests.md --- [Documentation](../../../index.md) / [@super-line/core](../index.md) / RoleRequests # Type Alias: RoleRequests\ > **RoleRequests**<`C`, `R`> = `CtsOf`<`C`\[`"roles"`]\[`R`]> Defined in: [packages/core/src/contract.ts:123](https://github.com/mertdogar/super-line/blob/130bd9adcf4898b57c097e3121e358d596527fa2/packages/core/src/contract.ts#L123) Requests in one role's block (not including `shared`). ## Type Parameters ### C `C` *extends* [`Contract`](../interfaces/Contract.md) ### R `R` *extends* [`RoleOf`](RoleOf.md)<`C`> --- --- url: >- https://super-line.dogar.biz/reference/@super-line/core/type-aliases/RoleTopics.md --- [Documentation](../../../index.md) / [@super-line/core](../index.md) / RoleTopics # Type Alias: RoleTopics\ > **RoleTopics**<`C`, `R`> = `TopicsOf`<`StcOf`<`C`\[`"roles"`]\[`R`]>> Defined in: [packages/core/src/contract.ts:129](https://github.com/mertdogar/super-line/blob/130bd9adcf4898b57c097e3121e358d596527fa2/packages/core/src/contract.ts#L129) Subscribable topics in one role's block (published via `srv.forRole(r).publish`). ## Type Parameters ### C `C` *extends* [`Contract`](../interfaces/Contract.md) ### R `R` *extends* [`RoleOf`](RoleOf.md)<`C`> --- --- url: 'https://super-line.dogar.biz/reference/@super-line/core/type-aliases/Schema.md' --- [Documentation](../../../index.md) / [@super-line/core](../index.md) / Schema # Type Alias: Schema > **Schema** = `StandardSchemaV1` Defined in: [packages/core/src/contract.ts:5](https://github.com/mertdogar/super-line/blob/130bd9adcf4898b57c097e3121e358d596527fa2/packages/core/src/contract.ts#L5) Any [Standard Schema](https://standardschema.dev) validator (Zod, Valibot, ArkType…). --- --- url: >- https://super-line.dogar.biz/reference/@super-line/core/type-aliases/SchemaConverter.md --- [Documentation](../../../index.md) / [@super-line/core](../index.md) / SchemaConverter # Type Alias: SchemaConverter > **SchemaConverter** = (`schema`) => `unknown` Defined in: [packages/core/src/inspector.ts:132](https://github.com/mertdogar/super-line/blob/130bd9adcf4898b57c097e3121e358d596527fa2/packages/core/src/inspector.ts#L132) A schema → JSON Schema converter (best-effort). Supplied by the server in slice 3. ## Parameters ### schema [`Schema`](Schema.md) ## Returns `unknown` --- --- url: >- https://super-line.dogar.biz/reference/@super-line/core/type-aliases/ServerEntry.md --- [Documentation](../../../index.md) / [@super-line/core](../index.md) / ServerEntry # Type Alias: ServerEntry > **ServerEntry** = [`ServerMessageDef`](../interfaces/ServerMessageDef.md) | [`ServerRequestDef`](../interfaces/ServerRequestDef.md) Defined in: [packages/core/src/contract.ts:43](https://github.com/mertdogar/super-line/blob/130bd9adcf4898b57c097e3121e358d596527fa2/packages/core/src/contract.ts#L43) A `serverToClient` entry: a push event, a subscribable topic, or a server→client request. --- --- url: >- https://super-line.dogar.biz/reference/@super-line/core/type-aliases/ServerFrame.md --- [Documentation](../../../index.md) / [@super-line/core](../index.md) / ServerFrame # Type Alias: ServerFrame > **ServerFrame** = [`ResFrame`](../interfaces/ResFrame.md) | [`ErrFrame`](../interfaces/ErrFrame.md) | [`EvtFrame`](../interfaces/EvtFrame.md) | [`PubFrame`](../interfaces/PubFrame.md) | [`SReqFrame`](../interfaces/SReqFrame.md) | [`SChangeFrame`](../interfaces/SChangeFrame.md) | [`PingFrame`](../interfaces/PingFrame.md) | [`PongFrame`](../interfaces/PongFrame.md) Defined in: [packages/core/src/wire.ts:126](https://github.com/mertdogar/super-line/blob/130bd9adcf4898b57c097e3121e358d596527fa2/packages/core/src/wire.ts#L126) --- --- url: >- https://super-line.dogar.biz/reference/@super-line/core/type-aliases/ServerInput.md --- [Documentation](../../../index.md) / [@super-line/core](../index.md) / ServerInput # Type Alias: ServerInput\ > **ServerInput**<`T`> = `T` *extends* [`RequestDef`](../interfaces/RequestDef.md) ? [`InferOut`](InferOut.md)<`T`\[`"input"`]> : `never` Defined in: [packages/core/src/contract.ts:149](https://github.com/mertdogar/super-line/blob/130bd9adcf4898b57c097e3121e358d596527fa2/packages/core/src/contract.ts#L149) The input type a server handler receives for a request (post-validation). ## Type Parameters ### T `T` --- --- url: >- https://super-line.dogar.biz/reference/@super-line/core/type-aliases/ServerMessages.md --- [Documentation](../../../index.md) / [@super-line/core](../index.md) / ServerMessages # Type Alias: ServerMessages\ > **ServerMessages**<`C`, `R`> = `StcOf`<`C`\[`"shared"`]> & `StcOf`<`C`\[`"roles"`]\[`R`]> Defined in: [packages/core/src/contract.ts:113](https://github.com/mertdogar/super-line/blob/130bd9adcf4898b57c097e3121e358d596527fa2/packages/core/src/contract.ts#L113) A role's effective server→client map (events and topics combined). ## Type Parameters ### C `C` *extends* [`Contract`](../interfaces/Contract.md) ### R `R` *extends* [`RoleOf`](RoleOf.md)<`C`> --- --- url: >- https://super-line.dogar.biz/reference/@super-line/core/type-aliases/ServerRequests.md --- [Documentation](../../../index.md) / [@super-line/core](../index.md) / ServerRequests # Type Alias: ServerRequests\ > **ServerRequests**<`C`, `R`> = `ServerReqOf`<[`ServerMessages`](ServerMessages.md)<`C`, `R`>> Defined in: [packages/core/src/contract.ts:132](https://github.com/mertdogar/super-line/blob/130bd9adcf4898b57c097e3121e358d596527fa2/packages/core/src/contract.ts#L132) A role's effective server→client requests (`shared` ∪ `roles[R]`), answered by `client.implement`. ## Type Parameters ### C `C` *extends* [`Contract`](../interfaces/Contract.md) ### R `R` *extends* [`RoleOf`](RoleOf.md)<`C`> --- --- url: >- https://super-line.dogar.biz/reference/@super-line/core/type-aliases/SharedEvents.md --- [Documentation](../../../index.md) / [@super-line/core](../index.md) / SharedEvents # Type Alias: SharedEvents\ > **SharedEvents**<`C`> = `EventsOf`<`StcOf`<`C`\[`"shared"`]>> Defined in: [packages/core/src/contract.ts:125](https://github.com/mertdogar/super-line/blob/130bd9adcf4898b57c097e3121e358d596527fa2/packages/core/src/contract.ts#L125) Push events in the `shared` block (broadcastable to a mixed-role room). ## Type Parameters ### C `C` *extends* [`Contract`](../interfaces/Contract.md) --- --- url: >- https://super-line.dogar.biz/reference/@super-line/core/type-aliases/SharedRequests.md --- [Documentation](../../../index.md) / [@super-line/core](../index.md) / SharedRequests # Type Alias: SharedRequests\ > **SharedRequests**<`C`> = `CtsOf`<`C`\[`"shared"`]> Defined in: [packages/core/src/contract.ts:121](https://github.com/mertdogar/super-line/blob/130bd9adcf4898b57c097e3121e358d596527fa2/packages/core/src/contract.ts#L121) Requests in the `shared` block (every role can call these). ## Type Parameters ### C `C` *extends* [`Contract`](../interfaces/Contract.md) --- --- url: >- https://super-line.dogar.biz/reference/@super-line/core/type-aliases/SharedServerRequests.md --- [Documentation](../../../index.md) / [@super-line/core](../index.md) / SharedServerRequests # Type Alias: SharedServerRequests\ > **SharedServerRequests**<`C`> = `ServerReqOf`<`StcOf`<`C`\[`"shared"`]>> Defined in: [packages/core/src/contract.ts:134](https://github.com/mertdogar/super-line/blob/130bd9adcf4898b57c097e3121e358d596527fa2/packages/core/src/contract.ts#L134) Server→client requests in the `shared` block (the surface `srv.toConn(id).request` can call). ## Type Parameters ### C `C` *extends* [`Contract`](../interfaces/Contract.md) --- --- url: >- https://super-line.dogar.biz/reference/@super-line/core/type-aliases/SharedTopics.md --- [Documentation](../../../index.md) / [@super-line/core](../index.md) / SharedTopics # Type Alias: SharedTopics\ > **SharedTopics**<`C`> = `TopicsOf`<`StcOf`<`C`\[`"shared"`]>> Defined in: [packages/core/src/contract.ts:127](https://github.com/mertdogar/super-line/blob/130bd9adcf4898b57c097e3121e358d596527fa2/packages/core/src/contract.ts#L127) Subscribable topics in the `shared` block (published via `srv.publish`). ## Type Parameters ### C `C` *extends* [`Contract`](../interfaces/Contract.md) --- --- url: >- https://super-line.dogar.biz/reference/@super-line/core/type-aliases/SuperLineErrorCode.md --- [Documentation](../../../index.md) / [@super-line/core](../index.md) / SuperLineErrorCode # Type Alias: SuperLineErrorCode > **SuperLineErrorCode** = `"BAD_REQUEST"` | `"UNAUTHORIZED"` | `"FORBIDDEN"` | `"NOT_FOUND"` | `"TIMEOUT"` | `"VALIDATION"` | `"DISCONNECTED"` | `"INTERNAL"` Defined in: [packages/core/src/errors.ts:2](https://github.com/mertdogar/super-line/blob/130bd9adcf4898b57c097e3121e358d596527fa2/packages/core/src/errors.ts#L2) The built-in error codes super-line uses across the wire. --- --- url: 'https://super-line.dogar.biz/reference/@super-line/core/type-aliases/Topics.md' --- [Documentation](../../../index.md) / [@super-line/core](../index.md) / Topics # Type Alias: Topics\ > **Topics**<`C`, `R`> = `TopicsOf`<[`ServerMessages`](ServerMessages.md)<`C`, `R`>> Defined in: [packages/core/src/contract.ts:118](https://github.com/mertdogar/super-line/blob/130bd9adcf4898b57c097e3121e358d596527fa2/packages/core/src/contract.ts#L118) A role's subscribable topics (server→client entries with `subscribe: true`). ## Type Parameters ### C `C` *extends* [`Contract`](../interfaces/Contract.md) ### R `R` *extends* [`RoleOf`](RoleOf.md)<`C`> --- --- url: >- https://super-line.dogar.biz/reference/@super-line/core/variables/INSPECTOR_ROLE.md --- [Documentation](../../../index.md) / [@super-line/core](../index.md) / INSPECTOR\_ROLE # Variable: INSPECTOR\_ROLE > `const` **INSPECTOR\_ROLE**: `"inspector"` = `'inspector'` Defined in: [packages/core/src/inspector.ts:10](https://github.com/mertdogar/super-line/blob/130bd9adcf4898b57c097e3121e358d596527fa2/packages/core/src/inspector.ts#L10) The reserved role minted for an inspector connection. --- --- url: >- https://super-line.dogar.biz/reference/@super-line/core/variables/INSPECTOR_SUBPROTOCOL.md --- [Documentation](../../../index.md) / [@super-line/core](../index.md) / INSPECTOR\_SUBPROTOCOL # Variable: INSPECTOR\_SUBPROTOCOL > `const` **INSPECTOR\_SUBPROTOCOL**: `"superline.inspector.v1"` = `'superline.inspector.v1'` Defined in: [packages/core/src/inspector.ts:7](https://github.com/mertdogar/super-line/blob/130bd9adcf4898b57c097e3121e358d596527fa2/packages/core/src/inspector.ts#L7) WS subprotocol the Control Center connects with; the server short-circuits auth for it. --- --- url: >- https://super-line.dogar.biz/reference/@super-line/core/variables/InspectorContract.md --- [Documentation](../../../index.md) / [@super-line/core](../index.md) / InspectorContract # Variable: InspectorContract > `const` **InspectorContract**: `object` Defined in: [packages/core/src/inspector.ts:114](https://github.com/mertdogar/super-line/blob/130bd9adcf4898b57c097e3121e358d596527fa2/packages/core/src/inspector.ts#L114) The fixed, library-owned contract describing the inspector surface. Identical for every super-line app, so it is NOT merged into the user's contract — inbound dispatch routes an inspector connection against this instead, which keeps the user's `RoleOf` clean. ## Type Declaration ### roles > `readonly` **roles**: `object` #### roles.inspector > `readonly` **inspector**: `object` #### roles.inspector.clientToServer > `readonly` **clientToServer**: `object` #### roles.inspector.clientToServer.getConn > `readonly` **getConn**: `object` #### roles.inspector.clientToServer.getConn.input > `readonly` **input**: `StandardSchemaV1`<{ `id`: `string`; }, { `id`: `string`; }> #### roles.inspector.clientToServer.getConn.output > `readonly` **output**: `StandardSchemaV1`<[`ConnView`](../interfaces/ConnView.md), [`ConnView`](../interfaces/ConnView.md)> #### roles.inspector.clientToServer.getContract > `readonly` **getContract**: `object` #### roles.inspector.clientToServer.getContract.input > `readonly` **input**: `StandardSchemaV1`<`void`, `void`> #### roles.inspector.clientToServer.getContract.output > `readonly` **output**: `StandardSchemaV1`<[`InspectedContract`](../interfaces/InspectedContract.md), [`InspectedContract`](../interfaces/InspectedContract.md)> #### roles.inspector.clientToServer.getNode > `readonly` **getNode**: `object` #### roles.inspector.clientToServer.getNode.input > `readonly` **input**: `StandardSchemaV1`<`void`, `void`> #### roles.inspector.clientToServer.getNode.output > `readonly` **output**: `StandardSchemaV1`<[`NodeView`](../interfaces/NodeView.md), [`NodeView`](../interfaces/NodeView.md)> #### roles.inspector.clientToServer.getTopology > `readonly` **getTopology**: `object` #### roles.inspector.clientToServer.getTopology.input > `readonly` **input**: `StandardSchemaV1`<`void`, `void`> #### roles.inspector.clientToServer.getTopology.output > `readonly` **output**: `StandardSchemaV1`<[`NodeStat`](../interfaces/NodeStat.md)\[], [`NodeStat`](../interfaces/NodeStat.md)\[]> #### roles.inspector.clientToServer.listConnections > `readonly` **listConnections**: `object` #### roles.inspector.clientToServer.listConnections.input > `readonly` **input**: `StandardSchemaV1`<`void`, `void`> #### roles.inspector.clientToServer.listConnections.output > `readonly` **output**: `StandardSchemaV1`<[`ConnDescriptor`](../interfaces/ConnDescriptor.md)\[], [`ConnDescriptor`](../interfaces/ConnDescriptor.md)\[]> #### roles.inspector.serverToClient > `readonly` **serverToClient**: `object` #### roles.inspector.serverToClient.events > `readonly` **events**: `object` #### roles.inspector.serverToClient.events.payload > `readonly` **payload**: `StandardSchemaV1`<[`InspectorEvent`](../type-aliases/InspectorEvent.md), [`InspectorEvent`](../type-aliases/InspectorEvent.md)> #### roles.inspector.serverToClient.events.subscribe > `readonly` **subscribe**: `true` = `true` --- --- url: >- https://super-line.dogar.biz/reference/@super-line/core/variables/jsonSerializer.md --- [Documentation](../../../index.md) / [@super-line/core](../index.md) / jsonSerializer # Variable: jsonSerializer > `const` **jsonSerializer**: [`Serializer`](../interfaces/Serializer.md) Defined in: [packages/core/src/serializer.ts:15](https://github.com/mertdogar/super-line/blob/130bd9adcf4898b57c097e3121e358d596527fa2/packages/core/src/serializer.ts#L15) The default serializer (`JSON`). Note: turns `Date` into a string — see the serialization guide. --- --- url: 'https://super-line.dogar.biz/reference/@super-line/core/variables/PROTOCOL.md' --- [Documentation](../../../index.md) / [@super-line/core](../index.md) / PROTOCOL # Variable: PROTOCOL > `const` **PROTOCOL**: `"superline.v1"` = `'superline.v1'` Defined in: [packages/core/src/wire.ts:7](https://github.com/mertdogar/super-line/blob/130bd9adcf4898b57c097e3121e358d596527fa2/packages/core/src/wire.ts#L7) Protocol version string, negotiated via the WebSocket subprotocol at upgrade. --- --- url: >- https://super-line.dogar.biz/reference/@super-line/core/functions/classifyContract.md --- [Documentation](../../../index.md) / [@super-line/core](../index.md) / classifyContract # Function: classifyContract() > **classifyContract**(`contract`, `convert?`): [`InspectedContract`](../interfaces/InspectedContract.md) Defined in: [packages/core/src/inspector.ts:178](https://github.com/mertdogar/super-line/blob/130bd9adcf4898b57c097e3121e358d596527fa2/packages/core/src/inspector.ts#L178) Walk a contract and project its structure: roles × directions × message names × flavors. Pass `convert` to attach best-effort JSON Schema to each message; omit it for structure only. ## Parameters ### contract [`Contract`](../interfaces/Contract.md) ### convert? [`SchemaConverter`](../type-aliases/SchemaConverter.md) ## Returns [`InspectedContract`](../interfaces/InspectedContract.md) --- --- url: >- https://super-line.dogar.biz/reference/@super-line/core/functions/defineContract.md --- [Documentation](../../../index.md) / [@super-line/core](../index.md) / defineContract # Function: defineContract() > **defineContract**<`C`>(`contract`): `C` Defined in: [packages/core/src/contract.ts:92](https://github.com/mertdogar/super-line/blob/130bd9adcf4898b57c097e3121e358d596527fa2/packages/core/src/contract.ts#L92) Define a contract. An identity function — `const` preserves literal keys and `subscribe: true` so the full surface can be inferred on both ends. ## Type Parameters ### C `C` *extends* [`Contract`](../interfaces/Contract.md) ## Parameters ### contract `C` ## Returns `C` ## Example ```ts import { z } from 'zod' import { defineContract } from '@super-line/core' export const api = defineContract({ shared: { clientToServer: { join: { input: z.object({ room: z.string() }), output: z.object({ ok: z.boolean() }) } }, serverToClient: { message: { payload: z.object({ text: z.string() }) } }, }, roles: { user: { clientToServer: { say: { input: z.object({ text: z.string() }), output: z.object({ id: z.string() }) } } }, agent: { clientToServer: { announce: { input: z.object({ text: z.string() }), output: z.object({ id: z.string() }) } } }, }, }) ``` --- --- url: 'https://super-line.dogar.biz/reference/@super-line/core/functions/validate.md' --- [Documentation](../../../index.md) / [@super-line/core](../index.md) / validate # Function: validate() > **validate**<`S`>(`schema`, `value`): `Promise`<`InferOutput`<`S`>> Defined in: [packages/core/src/contract.ts:170](https://github.com/mertdogar/super-line/blob/130bd9adcf4898b57c097e3121e358d596527fa2/packages/core/src/contract.ts#L170) Validate a value against a Standard Schema validator (sync or async). ## Type Parameters ### S `S` *extends* [`Schema`](../type-aliases/Schema.md) ## Parameters ### schema `S` the validator to run. ### value `unknown` the untrusted value to validate. ## Returns `Promise`<`InferOutput`<`S`>> the parsed, typed value. ## Throws [SuperLineError](../classes/SuperLineError.md) with code `VALIDATION` if the value doesn't match. --- --- url: >- https://super-line.dogar.biz/reference/@super-line/core/functions/validateSync.md --- [Documentation](../../../index.md) / [@super-line/core](../index.md) / validateSync # Function: validateSync() > **validateSync**<`S`>(`schema`, `value`): `InferOutput`<`S`> Defined in: [packages/core/src/contract.ts:190](https://github.com/mertdogar/super-line/blob/130bd9adcf4898b57c097e3121e358d596527fa2/packages/core/src/contract.ts#L190) Synchronous validation for hot paths (e.g. client inbound dispatch). ## Type Parameters ### S `S` *extends* [`Schema`](../type-aliases/Schema.md) ## Parameters ### schema `S` the validator to run. ### value `unknown` the untrusted value to validate. ## Returns `InferOutput`<`S`> the parsed, typed value. ## Throws [SuperLineError](../classes/SuperLineError.md) with code `VALIDATION` on mismatch, or `INTERNAL` if the schema is async. --- --- url: 'https://super-line.dogar.biz/reference/@super-line/react.md' --- [Documentation](../../index.md) / @super-line/react # @super-line/react React hooks for [**super-line**](https://mertdogar.github.io/super-line/) — typed `useRequest` / `useEvent` / `useSubscription` bound to a contract + role. ```bash pnpm add @super-line/react ``` ```tsx import { useState } from 'react' import { createSuperLineClient } from '@super-line/client' import { webSocketClientTransport } from '@super-line/transport-websocket' import { createSuperLineHooks } from '@super-line/react' import { api } from './contract' const { Provider, useRequest, useEvent, useSubscription } = createSuperLineHooks() function Root() { const [client] = useState(() => createSuperLineClient(api, { transport: webSocketClientTransport({ url: 'ws://localhost:3000' }), role: 'user', }), ) return } function Room() { const { call: send, isLoading } = useRequest('send') const presence = useSubscription('presence') useEvent('message', (m) => append(m)) // ... } ``` `react >= 18` is a peer dependency. Every hook is narrowed to the role passed to `createSuperLineHooks()`. * 📖 Docs: * 📚 Guide: [React](https://mertdogar.github.io/super-line/guide/react) * 📕 API reference: * 🧩 Source: MIT © Mert ## Interfaces * [RequestState](interfaces/RequestState.md) ## Functions * [createSuperLineHooks](functions/createSuperLineHooks.md) --- --- url: >- https://super-line.dogar.biz/reference/@super-line/react/interfaces/RequestState.md --- [Documentation](../../../index.md) / [@super-line/react](../index.md) / RequestState # Interface: RequestState\ Defined in: [index.ts:24](https://github.com/mertdogar/super-line/blob/130bd9adcf4898b57c097e3121e358d596527fa2/packages/react/src/index.ts#L24) State returned by `useRequest`. ## Type Parameters ### T `T` ## Properties ### data? > `optional` **data?**: `T` Defined in: [index.ts:26](https://github.com/mertdogar/super-line/blob/130bd9adcf4898b57c097e3121e358d596527fa2/packages/react/src/index.ts#L26) The last successful result, if any. *** ### error? > `optional` **error?**: `unknown` Defined in: [index.ts:28](https://github.com/mertdogar/super-line/blob/130bd9adcf4898b57c097e3121e358d596527fa2/packages/react/src/index.ts#L28) The last error thrown by `call`, if any. *** ### isLoading > **isLoading**: `boolean` Defined in: [index.ts:30](https://github.com/mertdogar/super-line/blob/130bd9adcf4898b57c097e3121e358d596527fa2/packages/react/src/index.ts#L30) Whether a `call` is in flight. --- --- url: >- https://super-line.dogar.biz/reference/@super-line/react/functions/createSuperLineHooks.md --- [Documentation](../../../index.md) / [@super-line/react](../index.md) / createSuperLineHooks # Function: createSuperLineHooks() > **createSuperLineHooks**<`C`, `R`>(): `object` Defined in: [index.ts:47](https://github.com/mertdogar/super-line/blob/130bd9adcf4898b57c097e3121e358d596527fa2/packages/react/src/index.ts#L47) Bind typed React hooks to a contract + role. Create the client once, wrap your tree in the returned ``, then use the hooks inside. ## Type Parameters ### C `C` *extends* [`Contract`](../../core/interfaces/Contract.md) ### R `R` *extends* `string` ## Returns ### Provider > **Provider**: (`props`) => `ReactNode` Provides a connected client to the hooks below. #### Parameters ##### props ###### children? `ReactNode` ###### client [`SuperLineClient`](../../client/type-aliases/SuperLineClient.md)<`C`, `R`> #### Returns `ReactNode` ### useClient > **useClient**: () => [`SuperLineClient`](../../client/type-aliases/SuperLineClient.md)<`C`, `R`> Access the client from context (throws outside a ``). #### Returns [`SuperLineClient`](../../client/type-aliases/SuperLineClient.md)<`C`, `R`> ### useEvent > **useEvent**: <`E`>(`event`, `handler`) => `void` Subscribe to a server-pushed event for the component's lifetime. #### Type Parameters ##### E `E` *extends* `string` | `number` | `symbol` #### Parameters ##### event `E` ##### handler (`data`) => `void` #### Returns `void` ### useRequest > **useRequest**: <`M`>(`method`) => [`RequestState`](../interfaces/RequestState.md)<[`Output`](../../core/type-aliases/Output.md)<[`Requests`](../../core/type-aliases/Requests.md)<`C`, `R`>\[`M`]>> & `object` Wrap a request as `{ data, error, isLoading, call }` for use in components. #### Type Parameters ##### M `M` *extends* `string` | `number` | `symbol` #### Parameters ##### method `M` #### Returns [`RequestState`](../interfaces/RequestState.md)<[`Output`](../../core/type-aliases/Output.md)<[`Requests`](../../core/type-aliases/Requests.md)<`C`, `R`>\[`M`]>> & `object` ### useResource > **useResource**: <`T`>(`name`, `id`) => `object` Open a Store Resource and track it reactively: returns its latest `data` (`undefined` until the catch-up snapshot arrives) plus `set`/`update` to write through. `data` is untyped — stores are off-contract (ADR-0003) — so pass `T` to assert its shape. The handle is closed on unmount. #### Type Parameters ##### T `T` = `unknown` #### Parameters ##### name `string` ##### id `string` #### Returns `object` ##### data > **data**: `T` | `undefined` ##### set > **set**: (`value`) => `void` ###### Parameters ###### value `T` ###### Returns `void` ##### update > **update**: (`partial`) => `void` ###### Parameters ###### partial `Partial`<`T`> ###### Returns `void` ### useSubscription > **useSubscription**: <`T`>(`topic`) => [`EventData`](../../core/type-aliases/EventData.md)<`TopicsOf`<[`ServerMessages`](../../core/type-aliases/ServerMessages.md)<`C`, `R`>>\[`T`]> | `undefined` Subscribe to a topic and return its latest value (or `undefined` before the first message). #### Type Parameters ##### T `T` *extends* `string` | `number` | `symbol` #### Parameters ##### topic `T` #### Returns [`EventData`](../../core/type-aliases/EventData.md)<`TopicsOf`<[`ServerMessages`](../../core/type-aliases/ServerMessages.md)<`C`, `R`>>\[`T`]> | `undefined` ## Example ```tsx const { Provider, useRequest, useEvent, useSubscription } = createSuperLineHooks() function Root() { const [client] = useState(() => createSuperLineClient(api, { url, role: 'user' })) return } ``` --- --- url: 'https://super-line.dogar.biz/reference/@super-line/server.md' --- [Documentation](../../index.md) / @super-line/server # @super-line/server The server for [**super-line**](https://mertdogar.github.io/super-line/) — end-to-end typesafe WebSockets for TypeScript. Implements a shared contract over [`ws`](https://www.npmjs.com/package/ws): role-keyed handlers, rooms, topics, middleware, lifecycle hooks, and node-to-node messaging. ```bash pnpm add @super-line/core @super-line/server @super-line/transport-websocket zod ``` ```ts import http from 'node:http' import { createSuperLineServer } from '@super-line/server' import { webSocketServerTransport } from '@super-line/transport-websocket' import { api } from './contract' const server = http.createServer() const srv = createSuperLineServer(api, { transports: [webSocketServerTransport({ server })], authenticate: (h) => ({ role: 'user' as const, ctx: { id: '1' } }), // throw -> 401 }) srv.implement({ user: { send: async ({ text }, ctx, conn) => { conn.emit('message', { text }) return { id: crypto.randomUUID() } }, }, }) server.listen(3000) ``` Authenticate receives the `Handshake` (`{ transport, headers, query, peer?, raw }`) and returns `{ role, ctx }`; cross-role calls are rejected with `NOT_FOUND`. The wire is carried by a pluggable transport — [`@super-line/transport-websocket`](https://www.npmjs.com/package/@super-line/transport-websocket) provides the WS transport shown above; other transports (HTTP/SSE, libp2p) are available — see the Transports guide. Scale across processes with [`@super-line/adapter-redis`](https://www.npmjs.com/package/@super-line/adapter-redis). * 📖 Docs: * 📚 Guides: [roles & auth](https://mertdogar.github.io/super-line/guide/roles-auth), [events & rooms](https://mertdogar.github.io/super-line/guide/events-rooms) * 📕 API reference: * 🧩 Source: MIT © Mert ## Classes * [Conn](classes/Conn.md) * [MemoryBus](classes/MemoryBus.md) ## Interfaces * [BusMeta](interfaces/BusMeta.md) * [ClusterView](interfaces/ClusterView.md) * [ConnTarget](interfaces/ConnTarget.md) * [LocalView](interfaces/LocalView.md) * [MiddlewareInfo](interfaces/MiddlewareInfo.md) * [RoleLens](interfaces/RoleLens.md) * [Room](interfaces/Room.md) * [ServerStoreHandle](interfaces/ServerStoreHandle.md) * [SuperLineServer](interfaces/SuperLineServer.md) * [SuperLineServerOptions](interfaces/SuperLineServerOptions.md) * [UserTarget](interfaces/UserTarget.md) ## Type Aliases * [AuthResult](type-aliases/AuthResult.md) * [Handlers](type-aliases/Handlers.md) * [Middleware](type-aliases/Middleware.md) ## Functions * [createInMemoryAdapter](functions/createInMemoryAdapter.md) * [createSuperLineServer](functions/createSuperLineServer.md) * [resolvePrincipal](functions/resolvePrincipal.md) --- --- url: 'https://super-line.dogar.biz/reference/@super-line/server/classes/Conn.md' --- [Documentation](../../../index.md) / [@super-line/server](../index.md) / Conn # Class: Conn\ Defined in: [conn.ts:11](https://github.com/mertdogar/super-line/blob/130bd9adcf4898b57c097e3121e358d596527fa2/packages/server/src/conn.ts#L11) A single client connection, passed to handlers as the third argument. Node-local: `conn` objects live on the node that accepted the connection, so don't stash one to reach a user later — cross-node delivery goes through the Adapter (use a per-user room instead). Generic over the events it may emit (scoped by role), its `ctx`, and its `role`. ## Type Parameters ### Ev `Ev` = `Record`<`string`, [`ServerMessageDef`](../../core/interfaces/ServerMessageDef.md)> ### Ctx `Ctx` = `unknown` ### Role `Role` *extends* `string` = `string` ### Data `Data` = `unknown` ## Constructors ### Constructor > **new Conn**<`Ev`, `Ctx`, `Role`, `Data`>(`raw`, `id`, `role`, `ctx`, `serializer`, `onEmit?`): `Conn`<`Ev`, `Ctx`, `Role`, `Data`> Defined in: [conn.ts:35](https://github.com/mertdogar/super-line/blob/130bd9adcf4898b57c097e3121e358d596527fa2/packages/server/src/conn.ts#L35) #### Parameters ##### raw [`RawConn`](../../core/interfaces/RawConn.md) The underlying transport connection. `conn.terminate()` simulates a drop in tests. ##### id `string` Server-assigned unique id for this connection (stable for its lifetime). ##### role `Role` This connection's role (the literal resolved by `authenticate`). ##### ctx `Ctx` The context `authenticate` returned for this connection. ##### serializer [`Serializer`](../../core/interfaces/Serializer.md) ##### onEmit? (`event`, `data`) => `void` Optional inspector tap: called with each `emit` so the server can mirror it to inspectors. #### Returns `Conn`<`Ev`, `Ctx`, `Role`, `Data`> ## Properties ### channels > `readonly` **channels**: `Set`<`string`> Defined in: [conn.ts:18](https://github.com/mertdogar/super-line/blob/130bd9adcf4898b57c097e3121e358d596527fa2/packages/server/src/conn.ts#L18) Namespaced channels (rooms + topics) this connection belongs to. *** ### connectedAt > `readonly` **connectedAt**: `number` Defined in: [conn.ts:27](https://github.com/mertdogar/super-line/blob/130bd9adcf4898b57c097e3121e358d596527fa2/packages/server/src/conn.ts#L27) When this connection was accepted (`Date.now()`). *** ### ctx > `readonly` **ctx**: `Ctx` Defined in: [conn.ts:43](https://github.com/mertdogar/super-line/blob/130bd9adcf4898b57c097e3121e358d596527fa2/packages/server/src/conn.ts#L43) The context `authenticate` returned for this connection. *** ### data > **data**: `Data` Defined in: [conn.ts:20](https://github.com/mertdogar/super-line/blob/130bd9adcf4898b57c097e3121e358d596527fa2/packages/server/src/conn.ts#L20) Mutable per-connection scratch state, typed per role by the contract's `data` schema. *** ### id > `readonly` **id**: `string` Defined in: [conn.ts:39](https://github.com/mertdogar/super-line/blob/130bd9adcf4898b57c097e3121e358d596527fa2/packages/server/src/conn.ts#L39) Server-assigned unique id for this connection (stable for its lifetime). *** ### lastPingAt? > `optional` **lastPingAt?**: `number` Defined in: [conn.ts:29](https://github.com/mertdogar/super-line/blob/130bd9adcf4898b57c097e3121e358d596527fa2/packages/server/src/conn.ts#L29) When the server last sent a heartbeat ping to this connection (managed by the server). *** ### lastPongAt? > `optional` **lastPongAt?**: `number` Defined in: [conn.ts:31](https://github.com/mertdogar/super-line/blob/130bd9adcf4898b57c097e3121e358d596527fa2/packages/server/src/conn.ts#L31) When a heartbeat pong was last received — liveness signal (managed by the server). *** ### missedPongs > **missedPongs**: `number` = `0` Defined in: [conn.ts:33](https://github.com/mertdogar/super-line/blob/130bd9adcf4898b57c097e3121e358d596527fa2/packages/server/src/conn.ts#L33) Pings sent since the last pong; drives reaping (managed by the server). *** ### principal? > `optional` **principal?**: `string` Defined in: [conn.ts:24](https://github.com/mertdogar/super-line/blob/130bd9adcf4898b57c097e3121e358d596527fa2/packages/server/src/conn.ts#L24) ACL identity for stores: `identify(conn) ?? conn.id`, set by the server at accept (always defined there). *** ### raw > `readonly` **raw**: [`RawConn`](../../core/interfaces/RawConn.md) Defined in: [conn.ts:37](https://github.com/mertdogar/super-line/blob/130bd9adcf4898b57c097e3121e358d596527fa2/packages/server/src/conn.ts#L37) The underlying transport connection. `conn.terminate()` simulates a drop in tests. *** ### role > `readonly` **role**: `Role` Defined in: [conn.ts:41](https://github.com/mertdogar/super-line/blob/130bd9adcf4898b57c097e3121e358d596527fa2/packages/server/src/conn.ts#L41) This connection's role (the literal resolved by `authenticate`). *** ### transport? > `optional` **transport?**: `string` Defined in: [conn.ts:22](https://github.com/mertdogar/super-line/blob/130bd9adcf4898b57c097e3121e358d596527fa2/packages/server/src/conn.ts#L22) The client↔server transport (wire) this connection was accepted on (set by the server at accept). ## Methods ### close() > **close**(): `void` Defined in: [conn.ts:68](https://github.com/mertdogar/super-line/blob/130bd9adcf4898b57c097e3121e358d596527fa2/packages/server/src/conn.ts#L68) Graceful close of the underlying transport connection. #### Returns `void` *** ### emit() > **emit**<`E`>(`event`, `data`): `void` Defined in: [conn.ts:62](https://github.com/mertdogar/super-line/blob/130bd9adcf4898b57c097e3121e358d596527fa2/packages/server/src/conn.ts#L62) Push an event to THIS connection (node-local). Scoped to the role's events. #### Type Parameters ##### E `E` *extends* `string` | `number` | `symbol` #### Parameters ##### event `E` ##### data [`EmitData`](../../core/type-aliases/EmitData.md)<`Ev`\[`E`]> #### Returns `void` *** ### send() > **send**(`frame`): `void` Defined in: [conn.ts:50](https://github.com/mertdogar/super-line/blob/130bd9adcf4898b57c097e3121e358d596527fa2/packages/server/src/conn.ts#L50) Encode and send a frame (unicast, e.g. req/res). #### Parameters ##### frame [`ServerFrame`](../../core/type-aliases/ServerFrame.md) #### Returns `void` *** ### sendRaw() > **sendRaw**(`payload`): `void` Defined in: [conn.ts:56](https://github.com/mertdogar/super-line/blob/130bd9adcf4898b57c097e3121e358d596527fa2/packages/server/src/conn.ts#L56) Forward an already-encoded frame (fan-out path; encoded once at the source). #### Parameters ##### payload `string` | `Uint8Array`<`ArrayBufferLike`> #### Returns `void` *** ### terminate() > **terminate**(): `void` Defined in: [conn.ts:73](https://github.com/mertdogar/super-line/blob/130bd9adcf4898b57c097e3121e358d596527fa2/packages/server/src/conn.ts#L73) Hard close with no handshake — used by heartbeat reaping. #### Returns `void` --- --- url: 'https://super-line.dogar.biz/reference/@super-line/server/classes/MemoryBus.md' --- [Documentation](../../../index.md) / [@super-line/server](../index.md) / MemoryBus # Class: MemoryBus Defined in: [memory-adapter.ts:9](https://github.com/mertdogar/super-line/blob/130bd9adcf4898b57c097e3121e358d596527fa2/packages/server/src/memory-adapter.ts#L9) In-process pub/sub bus. Share one bus across multiple servers to simulate multiple nodes in a test (each server gets its own adapter bound to the bus). The presence directory also lives here, so servers sharing a bus see the whole cluster (mirroring how Redis is shared in production). ## Constructors ### Constructor > **new MemoryBus**(): `MemoryBus` #### Returns `MemoryBus` ## Properties ### descriptors > `readonly` **descriptors**: `Map`<`string`, [`ConnDescriptor`](../../core/interfaces/ConnDescriptor.md)> Defined in: [memory-adapter.ts:12](https://github.com/mertdogar/super-line/blob/130bd9adcf4898b57c097e3121e358d596527fa2/packages/server/src/memory-adapter.ts#L12) ## Methods ### publish() > **publish**(`channel`, `payload`): `void` Defined in: [memory-adapter.ts:30](https://github.com/mertdogar/super-line/blob/130bd9adcf4898b57c097e3121e358d596527fa2/packages/server/src/memory-adapter.ts#L30) #### Parameters ##### channel `string` ##### payload `string` | `Uint8Array`<`ArrayBufferLike`> #### Returns `void` *** ### subscribe() > **subscribe**(`channel`, `adapter`): `void` Defined in: [memory-adapter.ts:14](https://github.com/mertdogar/super-line/blob/130bd9adcf4898b57c097e3121e358d596527fa2/packages/server/src/memory-adapter.ts#L14) #### Parameters ##### channel `string` ##### adapter `MemoryAdapter` #### Returns `void` *** ### unsubscribe() > **unsubscribe**(`channel`, `adapter`): `void` Defined in: [memory-adapter.ts:23](https://github.com/mertdogar/super-line/blob/130bd9adcf4898b57c097e3121e358d596527fa2/packages/server/src/memory-adapter.ts#L23) #### Parameters ##### channel `string` ##### adapter `MemoryAdapter` #### Returns `void` --- --- url: >- https://super-line.dogar.biz/reference/@super-line/server/interfaces/BusMeta.md --- [Documentation](../../../index.md) / [@super-line/server](../index.md) / BusMeta # Interface: BusMeta Defined in: [index.ts:129](https://github.com/mertdogar/super-line/blob/130bd9adcf4898b57c097e3121e358d596527fa2/packages/server/src/index.ts#L129) Metadata passed to a [SuperLineServer.subscribe](SuperLineServer.md#subscribe) callback alongside the event payload. ## Properties ### from > **from**: `string` Defined in: [index.ts:131](https://github.com/mertdogar/super-line/blob/130bd9adcf4898b57c097e3121e358d596527fa2/packages/server/src/index.ts#L131) The node that published the event. Equals `srv.nodeId` for a same-node publish (local echo). --- --- url: >- https://super-line.dogar.biz/reference/@super-line/server/interfaces/ClusterView.md --- [Documentation](../../../index.md) / [@super-line/server](../index.md) / ClusterView # Interface: ClusterView Defined in: [index.ts:175](https://github.com/mertdogar/super-line/blob/130bd9adcf4898b57c097e3121e358d596527fa2/packages/server/src/index.ts#L175) Asynchronous, cluster-wide introspection backed by the adapter's presence directory. Methods reject if the configured adapter has no presence support. ## Methods ### byUser() > **byUser**(`userId`): `Promise`<[`ConnDescriptor`](../../core/interfaces/ConnDescriptor.md)\[]> Defined in: [index.ts:181](https://github.com/mertdogar/super-line/blob/130bd9adcf4898b57c097e3121e358d596527fa2/packages/server/src/index.ts#L181) Connections for a given user key (the `identify` hook). #### Parameters ##### userId `string` #### Returns `Promise`<[`ConnDescriptor`](../../core/interfaces/ConnDescriptor.md)\[]> *** ### connections() > **connections**(): `Promise`<[`ConnDescriptor`](../../core/interfaces/ConnDescriptor.md)\[]> Defined in: [index.ts:177](https://github.com/mertdogar/super-line/blob/130bd9adcf4898b57c097e3121e358d596527fa2/packages/server/src/index.ts#L177) Every live connection across the cluster. #### Returns `Promise`<[`ConnDescriptor`](../../core/interfaces/ConnDescriptor.md)\[]> *** ### count() > **count**(): `Promise`<`number`> Defined in: [index.ts:179](https://github.com/mertdogar/super-line/blob/130bd9adcf4898b57c097e3121e358d596527fa2/packages/server/src/index.ts#L179) Total live connection count across the cluster. #### Returns `Promise`<`number`> *** ### room() > **room**(`name`): `Promise`<[`ConnDescriptor`](../../core/interfaces/ConnDescriptor.md)\[]> Defined in: [index.ts:183](https://github.com/mertdogar/super-line/blob/130bd9adcf4898b57c097e3121e358d596527fa2/packages/server/src/index.ts#L183) Connections that are members of `room`, across nodes. #### Parameters ##### name `string` #### Returns `Promise`<[`ConnDescriptor`](../../core/interfaces/ConnDescriptor.md)\[]> *** ### topology() > **topology**(): `Promise`<[`NodeStat`](../../core/interfaces/NodeStat.md)\[]> Defined in: [index.ts:185](https://github.com/mertdogar/super-line/blob/130bd9adcf4898b57c097e3121e358d596527fa2/packages/server/src/index.ts#L185) Per-node aggregates (the other nodes and their counts). #### Returns `Promise`<[`NodeStat`](../../core/interfaces/NodeStat.md)\[]> --- --- url: >- https://super-line.dogar.biz/reference/@super-line/server/interfaces/ConnTarget.md --- [Documentation](../../../index.md) / [@super-line/server](../index.md) / ConnTarget # Interface: ConnTarget\ Defined in: [index.ts:189](https://github.com/mertdogar/super-line/blob/130bd9adcf4898b57c097e3121e358d596527fa2/packages/server/src/index.ts#L189) A single targeted connection, reachable on whatever node holds it. ## Type Parameters ### C `C` *extends* [`Contract`](../../core/interfaces/Contract.md) ## Methods ### close() > **close**(): `void` Defined in: [index.ts:203](https://github.com/mertdogar/super-line/blob/130bd9adcf4898b57c097e3121e358d596527fa2/packages/server/src/index.ts#L203) Close this connection (cross-node kick). #### Returns `void` *** ### emit() > **emit**<`E`>(`event`, `data`): `void` Defined in: [index.ts:191](https://github.com/mertdogar/super-line/blob/130bd9adcf4898b57c097e3121e358d596527fa2/packages/server/src/index.ts#L191) Push a shared event to this connection (cross-node). #### Type Parameters ##### E `E` *extends* `string` | `number` | `symbol` #### Parameters ##### event `E` ##### data [`EmitData`](../../core/type-aliases/EmitData.md)<`EventsOf`<`StcOf`<`C`\[`"shared"`]>>\[`E`]> #### Returns `void` *** ### request() > **request**<`M`>(`name`, `input`, `opts?`): `Promise`<[`Output`](../../core/type-aliases/Output.md)<`ServerReqOf`<`StcOf`<`C`\[`"shared"`]>>\[`M`]>> Defined in: [index.ts:197](https://github.com/mertdogar/super-line/blob/130bd9adcf4898b57c097e3121e358d596527fa2/packages/server/src/index.ts#L197) Send a shared server→client request and await the client's typed reply (cross-node). Rejects with a `TIMEOUT` `SuperLineError` if no live node owns the connection or the client doesn't answer in time. #### Type Parameters ##### M `M` *extends* `string` | `number` | `symbol` #### Parameters ##### name `M` ##### input [`ClientInput`](../../core/type-aliases/ClientInput.md)<`ServerReqOf`<`StcOf`<`C`\[`"shared"`]>>\[`M`]> ##### opts? ###### signal? `AbortSignal` ###### timeout? `number` #### Returns `Promise`<[`Output`](../../core/type-aliases/Output.md)<`ServerReqOf`<`StcOf`<`C`\[`"shared"`]>>\[`M`]>> --- --- url: >- https://super-line.dogar.biz/reference/@super-line/server/interfaces/LocalView.md --- [Documentation](../../../index.md) / [@super-line/server](../index.md) / LocalView # Interface: LocalView Defined in: [index.ts:162](https://github.com/mertdogar/super-line/blob/130bd9adcf4898b57c097e3121e358d596527fa2/packages/server/src/index.ts#L162) Synchronous, node-local introspection of the current server process. ## Properties ### connections > `readonly` **connections**: [`Conn`](../classes/Conn.md)<`Record`<`string`, [`ServerMessageDef`](../../core/interfaces/ServerMessageDef.md)>, `unknown`, `string`, `unknown`>\[] Defined in: [index.ts:164](https://github.com/mertdogar/super-line/blob/130bd9adcf4898b57c097e3121e358d596527fa2/packages/server/src/index.ts#L164) Snapshot of all connections accepted on this node. *** ### rooms > `readonly` **rooms**: `string`\[] Defined in: [index.ts:166](https://github.com/mertdogar/super-line/blob/130bd9adcf4898b57c097e3121e358d596527fa2/packages/server/src/index.ts#L166) Names of rooms with at least one member on this node. *** ### topics > `readonly` **topics**: `string`\[] Defined in: [index.ts:168](https://github.com/mertdogar/super-line/blob/130bd9adcf4898b57c097e3121e358d596527fa2/packages/server/src/index.ts#L168) Names of topics with at least one subscriber on this node. --- --- url: >- https://super-line.dogar.biz/reference/@super-line/server/interfaces/MiddlewareInfo.md --- [Documentation](../../../index.md) / [@super-line/server](../index.md) / MiddlewareInfo # Interface: MiddlewareInfo Defined in: [index.ts:119](https://github.com/mertdogar/super-line/blob/130bd9adcf4898b57c097e3121e358d596527fa2/packages/server/src/index.ts#L119) Context passed to middleware and lifecycle hooks about the current operation. ## Properties ### conn? > `optional` **conn?**: [`Conn`](../classes/Conn.md)<`Record`<`string`, [`ServerMessageDef`](../../core/interfaces/ServerMessageDef.md)>, `unknown`, `string`, `unknown`> Defined in: [index.ts:125](https://github.com/mertdogar/super-line/blob/130bd9adcf4898b57c097e3121e358d596527fa2/packages/server/src/index.ts#L125) The connection the operation is on, if any (`conn.role` available). Absent for bus events. *** ### kind > **kind**: `"subscribe"` | `"request"` | `"event"` Defined in: [index.ts:121](https://github.com/mertdogar/super-line/blob/130bd9adcf4898b57c097e3121e358d596527fa2/packages/server/src/index.ts#L121) Whether this is a request, a topic subscribe, or a bus event delivery. *** ### name > **name**: `string` Defined in: [index.ts:123](https://github.com/mertdogar/super-line/blob/130bd9adcf4898b57c097e3121e358d596527fa2/packages/server/src/index.ts#L123) The request/topic/event name. --- --- url: >- https://super-line.dogar.biz/reference/@super-line/server/interfaces/RoleLens.md --- [Documentation](../../../index.md) / [@super-line/server](../index.md) / RoleLens # Interface: RoleLens\ Defined in: [index.ts:215](https://github.com/mertdogar/super-line/blob/130bd9adcf4898b57c097e3121e358d596527fa2/packages/server/src/index.ts#L215) Lens for role-scoped server sends, returned by `srv.forRole(role)`. ## Type Parameters ### C `C` *extends* [`Contract`](../../core/interfaces/Contract.md) ### R `R` *extends* [`RoleOf`](../../core/type-aliases/RoleOf.md)<`C`> ## Methods ### publish() > **publish**<`T`>(`topic`, `data`): `void` Defined in: [index.ts:217](https://github.com/mertdogar/super-line/blob/130bd9adcf4898b57c097e3121e358d596527fa2/packages/server/src/index.ts#L217) Publish to a topic in role `R`'s surface (reaches that role's subscribers). #### Type Parameters ##### T `T` *extends* `string` | `number` | `symbol` #### Parameters ##### topic `T` ##### data [`EmitData`](../../core/type-aliases/EmitData.md)<`TopicsOf`<`StcOf`<`C`\[`"roles"`]\[`R`]>>\[`T`]> #### Returns `void` --- --- url: 'https://super-line.dogar.biz/reference/@super-line/server/interfaces/Room.md' --- [Documentation](../../../index.md) / [@super-line/server](../index.md) / Room # Interface: Room\ Defined in: [index.ts:148](https://github.com/mertdogar/super-line/blob/130bd9adcf4898b57c097e3121e358d596527fa2/packages/server/src/index.ts#L148) A mixed-role, server-controlled connection group. `broadcast` delivers a **shared** event to every member, regardless of their role. ## Type Parameters ### C `C` *extends* [`Contract`](../../core/interfaces/Contract.md) ## Properties ### connections > `readonly` **connections**: [`Conn`](../classes/Conn.md)<`Record`<`string`, [`ServerMessageDef`](../../core/interfaces/ServerMessageDef.md)>, `unknown`, `string`, `unknown`>\[] Defined in: [index.ts:158](https://github.com/mertdogar/super-line/blob/130bd9adcf4898b57c097e3121e358d596527fa2/packages/server/src/index.ts#L158) Snapshot of this room's members **on the current node**. *** ### size > `readonly` **size**: `number` Defined in: [index.ts:156](https://github.com/mertdogar/super-line/blob/130bd9adcf4898b57c097e3121e358d596527fa2/packages/server/src/index.ts#L156) Member count **on the current node** (membership is node-local). ## Methods ### add() > **add**(`conn`): `void` Defined in: [index.ts:150](https://github.com/mertdogar/super-line/blob/130bd9adcf4898b57c097e3121e358d596527fa2/packages/server/src/index.ts#L150) Add a connection to the room (server-controlled membership). #### Parameters ##### conn [`Conn`](../classes/Conn.md) #### Returns `void` *** ### broadcast() > **broadcast**<`E`>(`event`, `data`): `void` Defined in: [index.ts:154](https://github.com/mertdogar/super-line/blob/130bd9adcf4898b57c097e3121e358d596527fa2/packages/server/src/index.ts#L154) Broadcast a shared event to all members. #### Type Parameters ##### E `E` *extends* `string` | `number` | `symbol` #### Parameters ##### event `E` ##### data [`EmitData`](../../core/type-aliases/EmitData.md)<`EventsOf`<`StcOf`<`C`\[`"shared"`]>>\[`E`]> #### Returns `void` *** ### remove() > **remove**(`conn`): `void` Defined in: [index.ts:152](https://github.com/mertdogar/super-line/blob/130bd9adcf4898b57c097e3121e358d596527fa2/packages/server/src/index.ts#L152) Remove a connection from the room. #### Parameters ##### conn [`Conn`](../classes/Conn.md) #### Returns `void` --- --- url: >- https://super-line.dogar.biz/reference/@super-line/server/interfaces/ServerStoreHandle.md --- [Documentation](../../../index.md) / [@super-line/server](../index.md) / ServerStoreHandle # Interface: ServerStoreHandle Defined in: [index.ts:225](https://github.com/mertdogar/super-line/blob/130bd9adcf4898b57c097e3121e358d596527fa2/packages/server/src/index.ts#L225) Server-side handle for one configured Store, reached via `srv.store.`. The server is authoritative: it creates Resources, grants/revokes access, and may co-write. `data` is untyped (stores are off-contract — see ADR-0003); callers assert the shape. ## Methods ### create() > **create**(`id`, `data`, `accessRules`): `Promise`<`void`> Defined in: [index.ts:227](https://github.com/mertdogar/super-line/blob/130bd9adcf4898b57c097e3121e358d596527fa2/packages/server/src/index.ts#L227) Create a Resource with initial data + access rules (deny-by-default for everyone unlisted). #### Parameters ##### id `string` ##### data `unknown` ##### accessRules [`AccessRules`](../../core/type-aliases/AccessRules.md) #### Returns `Promise`<`void`> *** ### delete() > **delete**(`id`): `Promise`<`void`> Defined in: [index.ts:237](https://github.com/mertdogar/super-line/blob/130bd9adcf4898b57c097e3121e358d596527fa2/packages/server/src/index.ts#L237) Delete a Resource. #### Parameters ##### id `string` #### Returns `Promise`<`void`> *** ### grant() > **grant**(`id`, `principal`, `perms`): `Promise`<`void`> Defined in: [index.ts:233](https://github.com/mertdogar/super-line/blob/130bd9adcf4898b57c097e3121e358d596527fa2/packages/server/src/index.ts#L233) Grant a principal read/write on a Resource. #### Parameters ##### id `string` ##### principal `string` ##### perms [`Perms`](../../core/interfaces/Perms.md) #### Returns `Promise`<`void`> *** ### list() > **list**(): `Promise`<`string`\[]> Defined in: [index.ts:239](https://github.com/mertdogar/super-line/blob/130bd9adcf4898b57c097e3121e358d596527fa2/packages/server/src/index.ts#L239) All Resource ids in this store. #### Returns `Promise`<`string`\[]> *** ### read() > **read**(`id`): `Promise`<[`Resource`](../../core/interfaces/Resource.md)<`unknown`> | `undefined`> Defined in: [index.ts:229](https://github.com/mertdogar/super-line/blob/130bd9adcf4898b57c097e3121e358d596527fa2/packages/server/src/index.ts#L229) Read a Resource (data + accessRules), or undefined if absent. #### Parameters ##### id `string` #### Returns `Promise`<[`Resource`](../../core/interfaces/Resource.md)<`unknown`> | `undefined`> *** ### revoke() > **revoke**(`id`, `principal`): `Promise`<`void`> Defined in: [index.ts:235](https://github.com/mertdogar/super-line/blob/130bd9adcf4898b57c097e3121e358d596527fa2/packages/server/src/index.ts#L235) Revoke a principal's access to a Resource entirely. #### Parameters ##### id `string` ##### principal `string` #### Returns `Promise`<`void`> *** ### write() > **write**(`id`, `data`): `Promise`<`void`> Defined in: [index.ts:231](https://github.com/mertdogar/super-line/blob/130bd9adcf4898b57c097e3121e358d596527fa2/packages/server/src/index.ts#L231) Server co-write: replace the Resource's value (LWW), fanned out to subscribers with a `server` origin. #### Parameters ##### id `string` ##### data `unknown` #### Returns `Promise`<`void`> --- --- url: >- https://super-line.dogar.biz/reference/@super-line/server/interfaces/SuperLineServer.md --- [Documentation](../../../index.md) / [@super-line/server](../index.md) / SuperLineServer # Interface: SuperLineServer\ Defined in: [index.ts:293](https://github.com/mertdogar/super-line/blob/130bd9adcf4898b57c097e3121e358d596527fa2/packages/server/src/index.ts#L293) A running super-line server, returned by [createSuperLineServer](../functions/createSuperLineServer.md). ## Type Parameters ### C `C` *extends* [`Contract`](../../core/interfaces/Contract.md) ### A `A` *extends* [`AuthResult`](../type-aliases/AuthResult.md)<`C`> ## Properties ### cluster > `readonly` **cluster**: [`ClusterView`](ClusterView.md) Defined in: [index.ts:301](https://github.com/mertdogar/super-line/blob/130bd9adcf4898b57c097e3121e358d596527fa2/packages/server/src/index.ts#L301) Asynchronous, cluster-wide introspection backed by the adapter's presence directory. *** ### local > `readonly` **local**: [`LocalView`](LocalView.md) Defined in: [index.ts:299](https://github.com/mertdogar/super-line/blob/130bd9adcf4898b57c097e3121e358d596527fa2/packages/server/src/index.ts#L299) Synchronous, node-local introspection (connections, rooms, topics on this process). *** ### nodeId > `readonly` **nodeId**: `string` Defined in: [index.ts:295](https://github.com/mertdogar/super-line/blob/130bd9adcf4898b57c097e3121e358d596527fa2/packages/server/src/index.ts#L295) This node's stable id (unique per server process). *** ### nodeName > `readonly` **nodeName**: `string` Defined in: [index.ts:297](https://github.com/mertdogar/super-line/blob/130bd9adcf4898b57c097e3121e358d596527fa2/packages/server/src/index.ts#L297) This node's friendly name (from `nodeName`/`SUPER_LINE_NODE_NAME`, else a short `nodeId` slice). ## Methods ### close() > **close**(): `Promise`<`void`> Defined in: [index.ts:328](https://github.com/mertdogar/super-line/blob/130bd9adcf4898b57c097e3121e358d596527fa2/packages/server/src/index.ts#L328) #### Returns `Promise`<`void`> *** ### forRole() > **forRole**<`R`>(`role`): [`RoleLens`](RoleLens.md)<`C`, `R`> Defined in: [index.ts:325](https://github.com/mertdogar/super-line/blob/130bd9adcf4898b57c097e3121e358d596527fa2/packages/server/src/index.ts#L325) Lens for role-scoped sends, e.g. forRole('user').publish('feed', data). #### Type Parameters ##### R `R` *extends* `string` #### Parameters ##### role `R` #### Returns [`RoleLens`](RoleLens.md)<`C`, `R`> *** ### implement() > **implement**(`handlers`): `SuperLineServer`<`C`, `A`> Defined in: [index.ts:309](https://github.com/mertdogar/super-line/blob/130bd9adcf4898b57c097e3121e358d596527fa2/packages/server/src/index.ts#L309) Register handlers for shared + per-role requests (chainable). #### Parameters ##### handlers [`Handlers`](../type-aliases/Handlers.md)<`C`, `A`> #### Returns `SuperLineServer`<`C`, `A`> *** ### isOnline() > **isOnline**(`userId`): `Promise`<`boolean`> Defined in: [index.ts:303](https://github.com/mertdogar/super-line/blob/130bd9adcf4898b57c097e3121e358d596527fa2/packages/server/src/index.ts#L303) Whether a user (by `identify` key) has at least one live connection anywhere. #### Parameters ##### userId `string` #### Returns `Promise`<`boolean`> *** ### publish() > **publish**<`T`>(`topic`, `data`): `void` Defined in: [index.ts:313](https://github.com/mertdogar/super-line/blob/130bd9adcf4898b57c097e3121e358d596527fa2/packages/server/src/index.ts#L313) Publish a SHARED topic to all subscribers (server-only publish). #### Type Parameters ##### T `T` *extends* `string` | `number` | `symbol` #### Parameters ##### topic `T` ##### data [`EmitData`](../../core/type-aliases/EmitData.md)<`TopicsOf`<`StcOf`<`C`\[`"shared"`]>>\[`T`]> #### Returns `void` *** ### room() > **room**(`name`): [`Room`](Room.md)<`C`> Defined in: [index.ts:311](https://github.com/mertdogar/super-line/blob/130bd9adcf4898b57c097e3121e358d596527fa2/packages/server/src/index.ts#L311) Mixed-role connection group; broadcast() sends a shared contract event to members. #### Parameters ##### name `string` #### Returns [`Room`](Room.md)<`C`> *** ### store() > **store**(`name`): [`ServerStoreHandle`](ServerStoreHandle.md) Defined in: [index.ts:327](https://github.com/mertdogar/super-line/blob/130bd9adcf4898b57c097e3121e358d596527fa2/packages/server/src/index.ts#L327) Server-authoritative handle for a configured Store (`srv.store('scene').create(...)`). Throws `NOT_FOUND` if the name isn't configured. #### Parameters ##### name `string` #### Returns [`ServerStoreHandle`](ServerStoreHandle.md) *** ### subscribe() > **subscribe**<`T`>(`topic`, `handler`): () => `void` Defined in: [index.ts:320](https://github.com/mertdogar/super-line/blob/130bd9adcf4898b57c097e3121e358d596527fa2/packages/server/src/index.ts#L320) Subscribe SERVER-side to a shared topic, cluster-wide. The callback fires for a publish from any node — including this one (local echo, delivered in-process with no round-trip). `meta.from` is the publishing node; self-exclude with `if (meta.from === srv.nodeId) return`. Returns an unsubscribe fn. #### Type Parameters ##### T `T` *extends* `string` | `number` | `symbol` #### Parameters ##### topic `T` ##### handler (`data`, `meta`) => `void` #### Returns () => `void` *** ### toConn() > **toConn**(`id`): [`ConnTarget`](ConnTarget.md)<`C`> Defined in: [index.ts:305](https://github.com/mertdogar/super-line/blob/130bd9adcf4898b57c097e3121e358d596527fa2/packages/server/src/index.ts#L305) Target a single connection by id, on whatever node holds it (cross-node emit/close). #### Parameters ##### id `string` #### Returns [`ConnTarget`](ConnTarget.md)<`C`> *** ### toUser() > **toUser**(`userId`): [`UserTarget`](UserTarget.md)<`C`> Defined in: [index.ts:307](https://github.com/mertdogar/super-line/blob/130bd9adcf4898b57c097e3121e358d596527fa2/packages/server/src/index.ts#L307) Target all of a user's connections (by `identify` key) across nodes (emit/disconnect). #### Parameters ##### userId `string` #### Returns [`UserTarget`](UserTarget.md)<`C`> --- --- url: >- https://super-line.dogar.biz/reference/@super-line/server/interfaces/SuperLineServerOptions.md --- [Documentation](../../../index.md) / [@super-line/server](../index.md) / SuperLineServerOptions # Interface: SuperLineServerOptions\ Defined in: [index.ts:243](https://github.com/mertdogar/super-line/blob/130bd9adcf4898b57c097e3121e358d596527fa2/packages/server/src/index.ts#L243) Options for [createSuperLineServer](../functions/createSuperLineServer.md). ## Type Parameters ### C `C` *extends* [`Contract`](../../core/interfaces/Contract.md) ### A `A` *extends* [`AuthResult`](../type-aliases/AuthResult.md)<`C`> ## Properties ### adapter? > `optional` **adapter?**: [`Adapter`](../../core/interfaces/Adapter.md) Defined in: [index.ts:249](https://github.com/mertdogar/super-line/blob/130bd9adcf4898b57c097e3121e358d596527fa2/packages/server/src/index.ts#L249) Cross-node fan-out adapter. Defaults to a per-server in-memory adapter. *** ### authenticate > **authenticate**: (`handshake`) => `Awaitable`<`A`> Defined in: [index.ts:256](https://github.com/mertdogar/super-line/blob/130bd9adcf4898b57c097e3121e358d596527fa2/packages/server/src/index.ts#L256) Authenticate a connection from its normalized [Handshake](../../core/interfaces/Handshake.md). Return { role, ctx }, or throw to reject. #### Parameters ##### handshake [`Handshake`](../../core/interfaces/Handshake.md) #### Returns `Awaitable`<`A`> *** ### authorizeSubscribe? > `optional` **authorizeSubscribe?**: (`topic`, `ctx`, `conn`) => `Awaitable`<`boolean` | `void`> Defined in: [index.ts:262](https://github.com/mertdogar/super-line/blob/130bd9adcf4898b57c097e3121e358d596527fa2/packages/server/src/index.ts#L262) Runs on each client subscribe. Return false or throw to deny. #### Parameters ##### topic `string` ##### ctx `CtxUnion`<`A`> ##### conn [`Conn`](../classes/Conn.md) #### Returns `Awaitable`<`boolean` | `void`> *** ### describeConn? > `optional` **describeConn?**: (`conn`) => `Record`<`string`, `unknown`> Defined in: [index.ts:260](https://github.com/mertdogar/super-line/blob/130bd9adcf4898b57c097e3121e358d596527fa2/packages/server/src/index.ts#L260) Extra fields merged into the connection's cluster descriptor (e.g. `{ plan }`). `ctx` is never auto-serialized. #### Parameters ##### conn [`Conn`](../classes/Conn.md) #### Returns `Record`<`string`, `unknown`> *** ### heartbeat? > `optional` **heartbeat?**: `false` | { `interval?`: `number`; `maxMissed?`: `number`; } Defined in: [index.ts:271](https://github.com/mertdogar/super-line/blob/130bd9adcf4898b57c097e3121e358d596527fa2/packages/server/src/index.ts#L271) Heartbeat: one timer pings every connection each `interval` ms (updating `conn.lastPingAt`/`lastPongAt`). Set `maxMissed` to terminate a connection that misses that many consecutive pongs. `false` disables it. Defaults to `{ interval: 30_000 }` (no reaping). *** ### identify? > `optional` **identify?**: (`conn`) => `string` | `undefined` Defined in: [index.ts:258](https://github.com/mertdogar/super-line/blob/130bd9adcf4898b57c097e3121e358d596527fa2/packages/server/src/index.ts#L258) Stable user key for a connection (powers `cluster.byUser`, `isOnline`, and `toUser`). #### Parameters ##### conn [`Conn`](../classes/Conn.md) #### Returns `string` | `undefined` *** ### inspector? > `optional` **inspector?**: `boolean` | { `redact?`: `string`\[]; } Defined in: [index.ts:277](https://github.com/mertdogar/super-line/blob/130bd9adcf4898b57c097e3121e358d596527fa2/packages/server/src/index.ts#L277) Enable the read-only Control Center inspector: emit `msg.*` telemetry and accept inspector clients. The WS transport must also be created with `inspector: true` to negotiate the `superline.inspector.v1` subprotocol. **Default off; dev / trusted-network only.** *** ### nodeName? > `optional` **nodeName?**: `string` Defined in: [index.ts:254](https://github.com/mertdogar/super-line/blob/130bd9adcf4898b57c097e3121e358d596527fa2/packages/server/src/index.ts#L254) Friendly name for this node, surfaced in `srv.nodeName`, the cluster descriptor, and the Control Center topology. Defaults to `SUPER_LINE_NODE_NAME` or a short slice of `nodeId`. *** ### onConnection? > `optional` **onConnection?**: (`conn`, `ctx`) => `void` Defined in: [index.ts:285](https://github.com/mertdogar/super-line/blob/130bd9adcf4898b57c097e3121e358d596527fa2/packages/server/src/index.ts#L285) Called once per accepted connection. #### Parameters ##### conn [`Conn`](../classes/Conn.md) ##### ctx `CtxUnion`<`A`> #### Returns `void` *** ### onDisconnect? > `optional` **onDisconnect?**: (`conn`, `ctx`, `code`) => `void` Defined in: [index.ts:287](https://github.com/mertdogar/super-line/blob/130bd9adcf4898b57c097e3121e358d596527fa2/packages/server/src/index.ts#L287) Called when a connection closes, with the WebSocket close `code`. #### Parameters ##### conn [`Conn`](../classes/Conn.md) ##### ctx `CtxUnion`<`A`> ##### code `number` #### Returns `void` *** ### onError? > `optional` **onError?**: (`error`, `info`) => `void` Defined in: [index.ts:289](https://github.com/mertdogar/super-line/blob/130bd9adcf4898b57c097e3121e358d596527fa2/packages/server/src/index.ts#L289) Called for any error thrown in middleware/handlers (after the client is replied to). #### Parameters ##### error `unknown` ##### info [`MiddlewareInfo`](MiddlewareInfo.md) #### Returns `void` *** ### serializer? > `optional` **serializer?**: [`Serializer`](../../core/interfaces/Serializer.md) Defined in: [index.ts:247](https://github.com/mertdogar/super-line/blob/130bd9adcf4898b57c097e3121e358d596527fa2/packages/server/src/index.ts#L247) Wire serializer; MUST match the client. Defaults to `jsonSerializer`. *** ### stores? > `optional` **stores?**: `Record`<`string`, [`ServerStore`](../../core/interfaces/ServerStore.md)> Defined in: [index.ts:283](https://github.com/mertdogar/super-line/blob/130bd9adcf4898b57c097e3121e358d596527fa2/packages/server/src/index.ts#L283) Pluggable persisted-state Stores, keyed by name (`{ scene: crdtStoreServer(), config: memoryStoreServer() }`). Each is the server half of a Store pair; the client passes the matching client halves. Surfaced as `srv.store.` and `client.store.`. Stores are off-contract and untyped (ADR-0003). *** ### transports > **transports**: [`ServerTransport`](../../core/interfaces/ServerTransport.md)\[] Defined in: [index.ts:245](https://github.com/mertdogar/super-line/blob/130bd9adcf4898b57c097e3121e358d596527fa2/packages/server/src/index.ts#L245) Client↔server transports to accept connections on (e.g. `webSocketServerTransport({ server })`). *** ### use? > `optional` **use?**: [`Middleware`](../type-aliases/Middleware.md)<`A`>\[] Defined in: [index.ts:264](https://github.com/mertdogar/super-line/blob/130bd9adcf4898b57c097e3121e358d596527fa2/packages/server/src/index.ts#L264) Middleware chain run before req/subscribe handlers (rate-limit, authz, logging, metrics). --- --- url: >- https://super-line.dogar.biz/reference/@super-line/server/interfaces/UserTarget.md --- [Documentation](../../../index.md) / [@super-line/server](../index.md) / UserTarget # Interface: UserTarget\ Defined in: [index.ts:207](https://github.com/mertdogar/super-line/blob/130bd9adcf4898b57c097e3121e358d596527fa2/packages/server/src/index.ts#L207) All of a user's connections (0..N devices), reachable across nodes. ## Type Parameters ### C `C` *extends* [`Contract`](../../core/interfaces/Contract.md) ## Methods ### disconnect() > **disconnect**(): `void` Defined in: [index.ts:211](https://github.com/mertdogar/super-line/blob/130bd9adcf4898b57c097e3121e358d596527fa2/packages/server/src/index.ts#L211) Disconnect every one of the user's connections (cross-node). #### Returns `void` *** ### emit() > **emit**<`E`>(`event`, `data`): `void` Defined in: [index.ts:209](https://github.com/mertdogar/super-line/blob/130bd9adcf4898b57c097e3121e358d596527fa2/packages/server/src/index.ts#L209) Push a shared event to every one of the user's connections (cross-node). #### Type Parameters ##### E `E` *extends* `string` | `number` | `symbol` #### Parameters ##### event `E` ##### data [`EmitData`](../../core/type-aliases/EmitData.md)<`EventsOf`<`StcOf`<`C`\[`"shared"`]>>\[`E`]> #### Returns `void` --- --- url: >- https://super-line.dogar.biz/reference/@super-line/server/type-aliases/AuthResult.md --- [Documentation](../../../index.md) / [@super-line/server](../index.md) / AuthResult # Type Alias: AuthResult\ > **AuthResult**<`C`> = `{ [R in RoleOf]: { ctx: unknown; role: R } }`\[[`RoleOf`](../../core/type-aliases/RoleOf.md)<`C`>] Defined in: [index.ts:64](https://github.com/mertdogar/super-line/blob/130bd9adcf4898b57c097e3121e358d596527fa2/packages/server/src/index.ts#L64) The discriminated value `authenticate` returns: a `role` (one of the contract's roles) plus its `ctx`. Returning different ctx shapes per role narrows both the handler surface and `ctx` together. ## Type Parameters ### C `C` *extends* [`Contract`](../../core/interfaces/Contract.md) --- --- url: >- https://super-line.dogar.biz/reference/@super-line/server/type-aliases/Handlers.md --- [Documentation](../../../index.md) / [@super-line/server](../index.md) / Handlers # Type Alias: Handlers\ > **Handlers**<`C`, `A`> = \[keyof [`SharedRequests`](../../core/type-aliases/SharedRequests.md)<`C`>] *extends* \[`never`] ? `object` : `object` & `{ [R in RoleOf]: RoleHandlers }` Defined in: [index.ts:112](https://github.com/mertdogar/super-line/blob/130bd9adcf4898b57c097e3121e358d596527fa2/packages/server/src/index.ts#L112) The handler map passed to `implement`: one block per role plus an optional `shared` block. Each handler's `input`/`ctx`/`conn` are narrowed to its role. The `shared` key is required only when the contract has shared requests. ## Type Parameters ### C `C` *extends* [`Contract`](../../core/interfaces/Contract.md) ### A `A` --- --- url: >- https://super-line.dogar.biz/reference/@super-line/server/type-aliases/Middleware.md --- [Documentation](../../../index.md) / [@super-line/server](../index.md) / Middleware # Type Alias: Middleware\ > **Middleware**<`A`> = (`ctx`, `info`, `next`) => `Awaitable`<`void`> Defined in: [index.ts:138](https://github.com/mertdogar/super-line/blob/130bd9adcf4898b57c097e3121e358d596527fa2/packages/server/src/index.ts#L138) Flat middleware run before request/subscribe handlers. Call `next()` to proceed, or `throw` to short-circuit (rejecting the operation). Does not change `ctx`'s type. ## Type Parameters ### A `A` ## Parameters ### ctx `CtxUnion`<`A`> ### info [`MiddlewareInfo`](../interfaces/MiddlewareInfo.md) ### next () => `Promise`<`void`> ## Returns `Awaitable`<`void`> --- --- url: >- https://super-line.dogar.biz/reference/@super-line/server/functions/createInMemoryAdapter.md --- [Documentation](../../../index.md) / [@super-line/server](../index.md) / createInMemoryAdapter # Function: createInMemoryAdapter() > **createInMemoryAdapter**(`bus?`): [`Adapter`](../../core/interfaces/Adapter.md) Defined in: [memory-adapter.ts:120](https://github.com/mertdogar/super-line/blob/130bd9adcf4898b57c097e3121e358d596527fa2/packages/server/src/memory-adapter.ts#L120) Create an in-memory [Adapter](../../core/interfaces/Adapter.md). The default for a single-node server. Pass a shared [MemoryBus](../classes/MemoryBus.md) to two servers to simulate cross-node fan-out. ## Parameters ### bus? [`MemoryBus`](../classes/MemoryBus.md) = `...` ## Returns [`Adapter`](../../core/interfaces/Adapter.md) --- --- url: >- https://super-line.dogar.biz/reference/@super-line/server/functions/createSuperLineServer.md --- [Documentation](../../../index.md) / [@super-line/server](../index.md) / createSuperLineServer # Function: createSuperLineServer() > **createSuperLineServer**<`C`, `A`>(`contract`, `opts`): [`SuperLineServer`](../interfaces/SuperLineServer.md)<`C`, `A`> Defined in: [index.ts:356](https://github.com/mertdogar/super-line/blob/130bd9adcf4898b57c097e3121e358d596527fa2/packages/server/src/index.ts#L356) Create a server bound to a contract. Attach it to an `http.Server`, then call [SuperLineServer.implement](../interfaces/SuperLineServer.md#implement) with your handlers. `authenticate` resolves each connection's `{ role, ctx }` at the upgrade. ## Type Parameters ### C `C` *extends* [`Contract`](../../core/interfaces/Contract.md) ### A `A` *extends* `object` ## Parameters ### contract `C` the shared contract. ### opts [`SuperLineServerOptions`](../interfaces/SuperLineServerOptions.md)<`C`, `A`> server options; `authenticate` is required. ## Returns [`SuperLineServer`](../interfaces/SuperLineServer.md)<`C`, `A`> the [SuperLineServer](../interfaces/SuperLineServer.md). ## Throws nothing directly; handler throws become a typed `SuperLineError` to the client. ## Example ```ts const srv = createSuperLineServer(api, { transports: [webSocketServerTransport({ server })], authenticate: (h) => ({ role: 'user' as const, ctx: { id: '1' } }), }) srv.implement({ shared: { join: async ({ room }, _ctx, conn) => { srv.room(room).add(conn); return { ok: true } } }, user: { say: async ({ text }, ctx) => ({ id: '...' }) }, }) ``` --- --- url: >- https://super-line.dogar.biz/reference/@super-line/server/functions/resolvePrincipal.md --- [Documentation](../../../index.md) / [@super-line/server](../index.md) / resolvePrincipal # Function: resolvePrincipal() > **resolvePrincipal**(`conn`, `identify?`): `string` Defined in: [conn.ts:83](https://github.com/mertdogar/super-line/blob/130bd9adcf4898b57c097e3121e358d596527fa2/packages/server/src/conn.ts#L83) The ACL principal for a connection: the stable `identify` key when configured, else the (random, per-connection) `conn.id`. Always returns a string — store access rules never key on `undefined`. Distinct from `identify`'s raw output (which may be undefined, used for presence). ## Parameters ### conn [`Conn`](../classes/Conn.md) ### identify? (`conn`) => `string` | `undefined` ## Returns `string` --- --- url: >- https://super-line.dogar.biz/adr/0001-automerge-over-yjs-for-synced-scene-state.md --- # ADR-0001: Use Automerge (not Yjs) as the CRDT for synced state * Status: **Superseded by [ADR-0002](./0002-yjs-via-super-store-over-automerge.md)** (2026-06-23) * Date: 2026-06-23 > **Superseded.** The CRDT engine was subsequently built as `super-store` on **Yjs**, which provides the plain-object ergonomics that tipped this decision toward Automerge — and avoids the WASM cost this ADR accepted. See ADR-0002. The z-order / fractional-index modelling below still stands (library-agnostic). ## Context super-line is gaining a synced-JSON-state primitive (a **Shared Document** — see CONTEXT.md). The driving use case is the OMMA designer: **N humans + N AI agents** co-editing a **Scene** (a plain-JSON design document), with the **server as a co-writer** and server-side persistence required. Authority is **reactive** (post-merge compensation), not preventive — acceptable because a design canvas tolerates minor conflict loss. Candidates evaluated against current docs: **Yjs** (the initial preference), **Automerge** (3.x), and **Loro** (mentioned as a fallback). Relevant facts about the state and writers: * The Scene is plain JSON: a collection of elements (each with a stable `id`), nested containers, structured props (numbers/enums/colors), and **plain-string** labels — essentially **no collaborative rich text**. * The AI agents *generate* structured granular mutations (`create`/`update`/`delete`/`reorder`). * The client scene is owned by an imperative renderer (`SkiaScene`), driven by a change-stream. * A prior, production-shaped Automerge `SceneDocStore` (server-authoritative hub, per-peer sync state, base64-over-JSON-WS, schema re-validation, an explicit "agent runs server-side / phase 5" TODO) already existed in `tomorrow-kits` and was **removed for priorities — not a technical blocker**. ## Decision Use **Automerge (3.x)** as the CRDT for the OMMA Scene sync, and as the **first CRDT binding super-line ships**. Rationale: 1. **Plain-JSON fit.** `change(doc, fn)` mutates plain objects/arrays with per-field merge for free — the lowest-friction model for agents emitting structured ops, and a 1:1 match for `SceneSource`. Yjs requires explicitly constructed shared types (or a third-party plain-object layer like `syncedStore`). 2. **Server-as-co-writer is first-class.** The server mutates with the same `change()` API, and `receiveSyncMessage` returns **path-addressed patches** (`{action, path, value}`) that drive reactive validation/compensation cleanly. `actorId` gives AI-vs-human attribution at the writer level. Yjs can do this via `observeDeep` + transaction origins, but with more wiring (echo-loop management). 3. **Yjs's strengths go unused here.** `Y.Text` (no rich text) and the provider ecosystem (own transport) don't pay off; meanwhile its costs (shared-type ceremony) are paid in full. 4. **Prior art.** A serious Automerge architecture already existed and was dropped only for priorities. The **move/reorder weakness is a wash** and did not influence the choice: neither library ships a usable native list-move (Yjs removed its experimental one; Automerge never shipped one). Both require the same fix. ## Consequences * **Accept the WASM cost.** Automerge ships a Rust→WASM core (hundreds of KB + async init + cold-start), vs Yjs's ~18 kB pure JS. This is the main downside. Mitigate: lazy-load on the client; watch serverless/edge cold-start on the server. *If* WASM weight ever becomes a hard constraint, the fallback is **Loro** (faster, smaller docs, native movable list — but younger). * **Pin the version.** Automerge 3.x is churny (`getHistory` removed; change-introspection helpers shifted). Pin and budget for migrations. * **Model z-order as keyed-map + fractional order**, not array index: elements as `{id → element}` with a per-element fractional `order` string, sorted at read time, so a reorder is a single LWW field write and concurrent moves can't duplicate. (Library-agnostic; the officially-blessed Automerge list pattern.) * **Persist via Automerge binary** (`save`/`saveIncremental`), NOT a JSON round-trip — the old prototype's `serialiseToInfer`-to-JSON persistence discarded history/attribution. JSON snapshots remain useful as a read-model/export, not as the store of record. * **Per-property attribution is not O(1)** in Automerge; store a `lastEditedBy` field per element if "who changed X" is a product feature. * **Keep super-line CRDT-agnostic at the plumbing layer.** super-line should relay opaque update bytes + persist + fan out over the adapter without hard-coding Automerge, so a **Yjs binding can follow later** (broader ecosystem mindshare serves more super-line users). Automerge is the first binding, not the only one. --- --- url: 'https://super-line.dogar.biz/adr/0002-yjs-via-super-store-over-automerge.md' --- # ADR-0002: Use Yjs (via super-store) as the CRDT binding, superseding Automerge * Status: Accepted * Date: 2026-06-23 * Supersedes: [ADR-0001](./0001-automerge-over-yjs-for-synced-scene-state.md) ## Context ADR-0001 chose **Automerge (3.x)** as super-line's first CRDT binding for the OMMA Scene, primarily for its plain-JSON `change(doc, fn)` ergonomics, and accepted Automerge's Rust→WASM weight (hundreds of KB + async init + cold-start) as the main downside. Since then the CRDT engine was actually built — as a **separate library, `super-store`** (`@super-store/store`, at `/Users/mertdogar/Workspace/personal/super-store`) — and it is built on **Yjs**, not Automerge. `super-store` exposes a single reactive primitive, `StoreValue`, that presents a **plain-object API over Yjs shared types**: plain objects → `Y.Map`, arrays → `Y.Array`, `Set`/`Map` → tagged `Y.Map`, with diff-and-patch writes inside one transaction, `observeDeep`-driven reactivity, and opt-in undo. It is the \[\[Store]]'s eventual `CrdtStore` engine: super-line `Store` → `CrdtStore` impl → `super-store StoreValue`. This contradicts ADR-0001, and the contradiction resolves *against* Automerge once `super-store` exists. ## Decision Use **Yjs, via `super-store`**, as the CRDT engine behind super-line's first `CrdtStore` implementation. Mark ADR-0001 **Superseded**. Rationale: 1. **The plain-JSON argument that favored Automerge is neutralized.** ADR-0001's decisive point was that Automerge merges plain objects for free while "Yjs requires explicitly constructed shared types (or a third-party plain-object layer like `syncedStore`)." `super-store` *is* that plain-object layer — already written, tested, and production-shaped — so Yjs now offers the same plain-object ergonomics that tipped the original decision. 2. **Yjs avoids ADR-0001's main accepted downside.** Yjs is ~18 kB of pure JS with no WASM core, async init, or cold-start penalty — directly removing the cost ADR-0001 flagged as its primary consequence (relevant for browser bundle size and serverless/edge cold-start). 3. **It reflects reality.** The engine is built. Keeping ADR-0001 Accepted would document a decision the codebase has already moved past. 4. **The Store layer stays CRDT-agnostic regardless.** super-line relays opaque \[\[Change]] bytes (see ADR-0003 / `docs/guide/synced-state.md`); the binding choice lives entirely inside the `CrdtStore` implementation, so an Automerge-backed `CrdtStore` remains possible later without touching core. This decision is about the *first* binding, not an exclusive one. ## Consequences * **The z-order / movable-list problem is unchanged.** ADR-0001 noted neither library ships a usable native list-move; that remains true for Yjs. Keep modelling order as a **keyed map + per-element fractional `order` string** (library-agnostic), exactly as ADR-0001's consequences prescribed. * **Per-actor attribution** comes from Yjs transaction origins / `clientID` rather than Automerge `actorId`; this is the CRDT-internal actor, distinct from super-line's \[\[Origin (writer id)]] and \[\[Principal (the ACL identity)]]. * **Persist via Yjs binary** (`Y.encodeStateAsUpdate` / incremental updates), not a JSON round-trip — same principle as ADR-0001 (preserve history/attribution; JSON snapshots are a read-model, not the store of record). * **super-store is a pre-1.0, private dependency** (`@super-store/store@0.1.0`). Its surface (and its documented "minor tweaks" vs the in-memory store — async fill-in, no-op on structurally-equal `set`, in-place mutation desyncs) becomes part of the `CrdtStore`'s contract. Pin and track it. * **A future Automerge `CrdtStore` is not foreclosed** — it would be an additional Store implementation, decided on its own merits if a use case (e.g. very large docs, native movable list via Loro/Automerge) demands it. --- --- url: 'https://super-line.dogar.biz/adr/0003-stores-are-off-contract-and-untyped.md' --- # ADR-0003: Stores are off-contract and untyped, outside the typed-contract spine * Status: Accepted * Date: 2026-06-23 ## Context super-line's defining identity is *"one contract, end-to-end types, no codegen; the server validates every inbound message."* Requests, events, and topics are all declared in `defineContract`, typed end to end, and schema-validated on the wire. The new \[\[Store]] primitive (see `CONTEXT.md`) could have followed the same path: declare named stores in the contract, give each a `data` schema, and generate typed, validated read/write methods. We chose **not** to. A future reader will reasonably ask why this one primitive breaks the house rule — hence this ADR. Two forces pushed off-contract: 1. **The Store is configured like a transport/adapter, not declared like a message.** A \[\[Store pair (server half / client half)]] is a runtime capability (memory / Redis / CRDT), chosen at `createSuperLineServer` / `createSuperLineClient` time. Its data shapes are an application concern that varies per Resource id at runtime, not a fixed per-message schema. 2. **CRDT stores make contract validation impossible *in principle*, not just inconvenient.** The symmetric \[\[Change]] carries an opaque `update` — for a `CrdtStore` that is a **binary merge delta**. You cannot validate a binary CRDT op against a JSON schema: it isn't the value, it's an instruction to mutate the value, and it may not even be applicable until merged. So even if we declared a `data` schema, the inbound `update` path could never honor it for the CRDT case — the exact case the Store exists to serve. ## Decision Stores are **off-contract and untyped at the wire**: * Stores are configured as runtime options (the server/client pairs), **not declared in `defineContract`**. * The generic store surface (`client.store..read/write/subscribe/open`) treats `data` as **caller-asserted** (`unknown` on the wire). * **Core does not schema-validate store `data`** — there is no contract schema for it, and CRDT `update` deltas are unvalidatable by construction. ## Consequences * **Store data loses super-line's two headline guarantees** — end-to-end types and "validate every inbound message." Callers assert `data` types themselves (`read`); a buggy or malicious client can write malformed `data` and core won't reject it at the wire. Per-store integrity is the Store implementation's and the application's responsibility. * **Hard gates must still route through typed requests.** Anything needing a real, preventive check (money, permissions, invariants) goes through a normal contract request — the same guidance `docs/guide/synced-state.md` already gives for CRDT state, and consistent with \[\[Access control (accessRules)]] being enforced server-side in request-handler code. * **The typed-contract spine is unchanged for requests/events/topics.** This ADR scopes the exception to store *data*; everything else keeps full typing + validation. ACL enforcement, fan-out, and the `store.*` inspector events are still core-owned. * **A typed-store option is not foreclosed for LWW stores.** A future contract-declared, schema-validated store could be added for last-writer-wins stores (where `update` *is* the value and so *is* validatable) — but it could never extend to CRDT deltas, so it would be an opt-in addition, not the default. * **DX is that of a generic store client** (think a permissioned, real-time KV/document client) rather than a typed RPC surface — the deliberate trade chosen for maximum flexibility and CRDT compatibility. --- --- url: 'https://super-line.dogar.biz/reference.md' --- # Documentation ## Packages * [@super-line/adapter-libp2p](@super-line/adapter-libp2p/index.md) * [@super-line/adapter-rabbitmq](@super-line/adapter-rabbitmq/index.md) * [@super-line/adapter-redis](@super-line/adapter-redis/index.md) * [@super-line/adapter-zeromq](@super-line/adapter-zeromq/index.md) * [@super-line/client](@super-line/client/index.md) * [@super-line/core](@super-line/core/index.md) * [@super-line/react](@super-line/react/index.md) * [@super-line/server](@super-line/server/index.md) --- --- url: 'https://super-line.dogar.biz/examples.md' --- # Examples Runnable examples live in [`examples/`](https://github.com/mertdogar/super-line/tree/main/examples). Clone the repo and run `pnpm install` first. ## chat — roles in one room A human (`user`) and an AI participant (`agent`) join the **same room** with different surfaces. Shows a `shared` `join` + `message` event, role-specific verbs (`say` vs `announce`), and `conn.role`. ```bash pnpm --filter @super-line/example-chat start ``` Demonstrates: [roles](/guide/roles-auth), [shared requests](/guide/requests), [events & rooms](/guide/events-rooms). ## react-chat — browser app A live React chat (Vite + a WS server). Open two browser tabs to chat in real time; shows the [React hooks](/guide/react), a presence [topic](/guide/topics), and a room broadcast. ```bash pnpm --filter @super-line/example-react-chat dev # http://localhost:5173 ``` ## store — a permissioned document store A scripted, single-process demo of the [Store](/guide/store) primitive on the in-memory LWW backend. The server creates a permissioned note and assigns per-user access; two users open it and one's write reaches the other live; a read-only user is denied a write (`FORBIDDEN`); a third user can't open the doc until the server grants access at runtime; and the server co-writes the document. ```bash pnpm --filter @super-line/example-store start ``` Demonstrates: [stores](/guide/store), [roles](/guide/roles-auth), [errors](/guide/errors). ## store-sync-json — a collaborative JSON editor (CRDT) A React app over the [CRDT Store](/guide/synced-state) (`@super-line/store-sync` — Yjs via super-store): a [`@visual-json`](https://visual-json.dev) editor bound to one shared Resource via [`useResource`](/guide/react). Open two tabs (or add `?name=bob`), edit any field, and watch edits **merge** live — concurrent edits to different fields both survive, unlike last-writer-wins. **Server nudge** triggers a server co-write. ```bash pnpm --filter @super-line/example-store-sync-json dev # http://localhost:5273 ``` Demonstrates: [synced state (CRDT)](/guide/synced-state), [stores](/guide/store), [React hooks](/guide/react). ## synced-canvas — roll-your-own CRDT (no Store seam) Two browser apps demonstrating **synced JSON state over super-line, backed by a CRDT** — a collaborative canvas where multiple tabs *and the server* co-edit one document, persisted server-side. super-line stays CRDT-agnostic: it relays opaque base64 update bytes per room and never parses the doc. A debug side panel mirrors the live state and logs each patch tagged by origin (`local` / `peer` / `server`), so you can watch the server's edits land. Built once with [Yjs](https://github.com/yjs/yjs) and once with [Automerge](https://automerge.org) — open either in two windows (run one at a time). ```bash pnpm --filter @super-line/example-synced-canvas-yjs dev # http://localhost:5173 pnpm --filter @super-line/example-synced-canvas-automerge dev # http://localhost:5173 ``` Demonstrates: [synced state (CRDT)](/guide/synced-state), [events & rooms](/guide/events-rooms), [requests](/guide/requests). ## hono — one server for HTTP + WebSockets super-line attached to a [Hono](https://hono.dev) app (`@hono/node-server`) on **one process, one port**: Hono serves the built frontend and REST routes while super-line owns the WebSocket bus, both on the same Node `http.Server` (the `{ server }` option — no library changes). Three live cards — a server-uptime [topic](/guide/topics), shared todos (req/res + a topic), and shared cursors whose identity is assigned server-side into `ctx` — plus a `POST /api/todos` **REST→WS bridge**: `curl` a todo in and watch it appear in every open tab. The bridge route and the WS upgrade share one auth rule. Open a few tabs and move your mouse. ```bash pnpm --filter @super-line/example-hono build pnpm --filter @super-line/example-hono start # http://localhost:3000 ``` Demonstrates: [topics](/guide/topics), [requests](/guide/requests), [middleware & lifecycle](/guide/middleware-lifecycle), composing with an HTTP framework. ## auth — roles as an authorization boundary Token auth with an `admin` and a `user` role. `whoami` is shared; `secret` is admin-only. A user calling `secret` gets `NOT_FOUND`; a bad token is rejected at the upgrade. ```bash pnpm --filter @super-line/example-auth start ``` Demonstrates: [auth](/guide/roles-auth), [`NOT_FOUND` enforcement](/guide/roles-auth#enforcement-not-found), [errors](/guide/errors). ## presence — introspection, targeted send & server→client requests Boots **two nodes** sharing one in-memory bus (no Docker needed) and shows the server-side toolkit across nodes: `cluster.count`/`topology`/`isOnline`, a `toUser(...).emit` from the node that *doesn't* hold the socket, and a `toConn(id).request(...)` where one node asks a client a question and awaits the typed reply (the client answers via `client.implement`). ```bash pnpm --filter @super-line/example-presence start ``` Demonstrates: [introspection & presence](/guide/introspection-and-presence). ## event-bus — single-process cluster event bus One process shows the [cluster event bus](/guide/cluster-event-bus) on a shared topic: a `server.publish` fans out to several in-process `server.subscribe` listeners (showing local echo — your own publish fires in-process, no round-trip) plus one client subscriber over WS. No Redis needed. ```bash pnpm --filter @super-line/example-event-bus start ``` Demonstrates: [the cluster event bus](/guide/cluster-event-bus). ## bus-cluster — multi-node server.subscribe showcase A cluster via Docker Compose: **Redis + Caddy + 3 server nodes + watcher clients**. Every node bumps a counter and `server.subscribe`s to every node's bumps, converging a shared tally — own bumps land in-process via local echo, peers' arrive over Redis. node-1 publishes a client-facing `total` snapshot. Needs Docker. ```bash cd examples/bus-cluster && docker compose up ``` Demonstrates: [the cluster event bus](/guide/cluster-event-bus), [scaling & adapters](/guide/scaling-adapters). ## scaling — a real multi-node cluster A genuine cluster via Docker Compose: **Redis + a Caddy load balancer + 3 server nodes + 6 client containers**. Caddy round-robins each client onto a node; you watch room broadcasts, a topic, and `stats` gossip — migrated to a shared `stats` topic over the cluster event bus — fan out across separate processes. Needs Docker. ```bash cd examples/scaling && docker compose up ``` See [`examples/scaling/README.md`](https://github.com/mertdogar/super-line/tree/main/examples/scaling) for what to watch, how to connect your own client to the load balancer, and `--scale`. Demonstrates: [scaling & adapters](/guide/scaling-adapters), [the cluster event bus](/guide/cluster-event-bus). ## react-chat-cluster — the browser app, across two servers [`react-chat`](#react-chat-browser-app) behind a real cluster via Docker Compose: **Redis + 2 server nodes + a Caddy** that serves the built SPA *and* round-robins `/ws` across the nodes. Open `http://localhost:8080` in several tabs — each lands on a different node (shown in the header), yet messages cross servers via a `room.broadcast` over the Redis adapter, and the online count is cluster-wide via `cluster.room(...)`. Needs Docker. ```bash cd examples/react-chat-cluster && docker compose up ``` See [`examples/react-chat-cluster/README.md`](https://github.com/mertdogar/super-line/tree/main/examples/react-chat-cluster) for the topology and what each tab shows. Demonstrates: [React hooks](/guide/react), [scaling & adapters](/guide/scaling-adapters), [introspection & presence](/guide/introspection-and-presence). ## react-chat-cluster-libp2p — the same cluster, no broker The same browser app and Control Center as `react-chat-cluster` above, but with **no Redis** — the two nodes peer directly over libp2p gossipsub via [`@super-line/adapter-libp2p`](/guide/adapter-libp2p). Same React SPA, same cross-node messages and cluster-wide (gossip-replicated) presence; the only structural change is the adapter line. Ports are offset (web `:8090`, Control Center `:8091`) so it runs alongside the Redis variant. Needs Docker. ```bash cd examples/react-chat-cluster-libp2p && docker compose up --build ``` Demonstrates: [React hooks](/guide/react), [scaling & adapters](/guide/adapter-libp2p), [introspection & presence](/guide/introspection-and-presence). --- --- url: 'https://super-line.dogar.biz/PRODUCT.md' --- # Product ## Register brand ## Users TypeScript developers evaluating or adopting a realtime layer for their app — the kind who reach for tRPC, Zod, and end-to-end type safety by reflex. They arrive from GitHub, a tweet, or a "typesafe websockets" search, usually while comparing options (Socket.IO, plain `ws`, tRPC subscriptions). Their context is a code editor in the other window: they want to judge in under a minute whether the contract model and the inference are real, then get to a working server + client fast. The audience is technical and skeptical — claims don't land, working code does. The job to be done: **decide that super-line is worth adopting, then get productive.** The home page must earn the click to "Get started"; the guides must get them to a typed round-trip without friction; the API reference must be trustworthy enough to live in day-to-day. ## Product Purpose super-line is an end-to-end typesafe WebSocket library for TypeScript. You write one contract — split by direction (`clientToServer` / `serverToClient`) and scoped by role (`user`, `agent`, `shared`) — and the server implements it while the client calls it with full inference and zero codegen. Requests, server-pushed events, rooms, client-subscribed topics, and a cluster-wide event bus all share one connection and fan out across processes via a pluggable adapter (in-memory or Redis). It ships with presence/introspection and a Control Center debug webapp. This docs site is the front door: a home-page hero that makes the contract-first, role-scoped pitch land instantly; a guide track that walks from first round-trip to scaling and AI agents; and a generated API reference. Success = a visiting developer understands the contract model from the hero, reaches a working example without getting lost, and trusts the project enough to `pnpm add` it. ## Brand Personality Fast & modern, precise & engineered, and bold & opinionated. The voice is a confident senior engineer who has strong, defensible opinions and proves them with code rather than adjectives. Tone is direct and exact — every term means something (contract, role, direction, topic, room, bus). The waveform mark is the spine of the identity: a clean signal on a line, realtime data made visible. The surface should feel premium and considered (Stripe-grade craft and information architecture) while staying unmistakably a developer tool — code is always the hero, never decoration around it. ## Anti-references * **Stock VitePress default** — the indigo-on-white, every-OSS-site-looks-the-same baseline. We start from VitePress but the theme must carry super-line's own cyan identity, not the framework's. * **SaaS-gray slop** — timid neutral palette, generic feature-card grid, a hero metric and three rounded-icon cards. Safe reads as invisible here. * **Borrowing Stripe's palette.** Stripe is the reference for *polish, generosity, and IA* — not for purple-on-white. The accent stays cyan; the lesson is craft, not color. * **Editorial-magazine cosplay** — display-serif + italic drop caps + ruled broadsheet columns. This is a dev tool, not a literary journal. * **Gradient-text headlines, glassmorphism, and decorative monospace.** Mono is for code, not for "looking technical." ## Design Principles 1. **The signal is the brand.** The waveform/EKG line is the recurring motif — a clean cyan signal on a steady line. Realtime, made visible. Use it as the identity thread, not as scattered decoration. 2. **Show the types working.** Code samples and live inference are the hero of the home page and every guide. Demonstrate the round-trip; never claim type safety in prose where a snippet would prove it. 3. **Premium, never ceremonial.** Match Stripe's craft and information architecture — generous spacing, clear hierarchy, trustworthy reference layout — but a developer should never wait on chrome or animation to reach the code. 4. **One confident accent.** Cyan carries the whole identity. Don't hedge into neutral gray for "professionalism"; commit to the color and let it do the work. 5. **Bold but legible.** Opinionated and distinct, yet body copy and code always meet AA contrast in both themes. Strangeness lives in composition and the brand motif, never at the cost of readability. ## Accessibility & Inclusion * **WCAG 2.1 AA** across both themes: body text ≥ 4.5:1, large text ≥ 3:1, against its actual background (including tinted code surfaces). * **Ship both light and dark themes** to a polished bar — dark is where the cyan mark sings, but light must be equally legible, not an afterthought. * **Cyan is never the only signal.** Pair color with text, weight, or icon for any state (active nav, callout type, link emphasis) so color-vision-deficient and grayscale readers don't lose meaning. * **Respect `prefers-reduced-motion`** — every entrance/reveal has a crossfade or instant fallback. * Keyboard-navigable nav, search, and code-copy controls with visible focus states.