@super-line/server 0.3.0 → 0.5.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +5 -4
- package/dist/index.cjs +239 -122
- package/dist/index.d.cts +60 -34
- package/dist/index.d.ts +60 -34
- package/dist/index.js +237 -122
- package/package.json +12 -8
package/dist/index.d.cts
CHANGED
|
@@ -1,25 +1,16 @@
|
|
|
1
|
-
import {
|
|
2
|
-
import { ServerMessageDef, Serializer, ServerFrame, EmitData, ConnDescriptor, Adapter, PresenceStore, Contract, RoleOf, NodeStat, SharedEvents, SharedServerRequests, ClientInput, Output, SharedRequests, ServerInput, AnyData, RoleRequests, Events, DataOf, RoleTopics, SharedTopics, EventData } from '@super-line/core';
|
|
3
|
-
import { WebSocket } from 'ws';
|
|
1
|
+
import { ServerMessageDef, RawConn, Serializer, ServerFrame, EmitData, ConnDescriptor, Adapter, PresenceStore, Contract, RoleOf, NodeStat, SharedEvents, SharedServerRequests, ClientInput, Output, SharedRequests, ServerInput, AnyData, RoleRequests, Events, DataOf, RoleTopics, AccessRules, Resource, Perms, SharedTopics, EventData, ServerTransport, Handshake, ServerStore } from '@super-line/core';
|
|
4
2
|
|
|
5
|
-
/** Backpressure policy: what to do when a connection's send buffer grows too large. */
|
|
6
|
-
interface Backpressure {
|
|
7
|
-
/** Buffer size (bytes) above which {@link Backpressure.onExceed} kicks in. */
|
|
8
|
-
maxBufferedBytes: number;
|
|
9
|
-
/** `'close'` (default) drops the connection with code 1013; `'drop'` skips the frame. */
|
|
10
|
-
onExceed?: 'close' | 'drop';
|
|
11
|
-
}
|
|
12
3
|
/**
|
|
13
4
|
* A single client connection, passed to handlers as the third argument.
|
|
14
5
|
*
|
|
15
|
-
* Node-local: `conn` objects live on the node that accepted the
|
|
6
|
+
* Node-local: `conn` objects live on the node that accepted the connection, so don't
|
|
16
7
|
* stash one to reach a user later — cross-node delivery goes through the Adapter
|
|
17
8
|
* (use a per-user room instead). Generic over the events it may emit (scoped by
|
|
18
9
|
* role), its `ctx`, and its `role`.
|
|
19
10
|
*/
|
|
20
11
|
declare class Conn<Ev = Record<string, ServerMessageDef>, Ctx = unknown, Role extends string = string, Data = unknown> {
|
|
21
|
-
/** The underlying
|
|
22
|
-
readonly
|
|
12
|
+
/** The underlying transport connection. `conn.terminate()` simulates a drop in tests. */
|
|
13
|
+
readonly raw: RawConn;
|
|
23
14
|
/** Server-assigned unique id for this connection (stable for its lifetime). */
|
|
24
15
|
readonly id: string;
|
|
25
16
|
/** This connection's role (the literal resolved by `authenticate`). */
|
|
@@ -27,14 +18,17 @@ declare class Conn<Ev = Record<string, ServerMessageDef>, Ctx = unknown, Role ex
|
|
|
27
18
|
/** The context `authenticate` returned for this connection. */
|
|
28
19
|
readonly ctx: Ctx;
|
|
29
20
|
private readonly serializer;
|
|
30
|
-
private readonly backpressure?;
|
|
31
21
|
/** Optional inspector tap: called with each `emit` so the server can mirror it to inspectors. */
|
|
32
22
|
private readonly onEmit?;
|
|
33
23
|
/** Namespaced channels (rooms + topics) this connection belongs to. */
|
|
34
24
|
readonly channels: Set<string>;
|
|
35
25
|
/** Mutable per-connection scratch state, typed per role by the contract's `data` schema. */
|
|
36
26
|
data: Data;
|
|
37
|
-
/**
|
|
27
|
+
/** The client↔server transport (wire) this connection was accepted on (set by the server at accept). */
|
|
28
|
+
transport?: string;
|
|
29
|
+
/** ACL identity for stores: `identify(conn) ?? conn.id`, set by the server at accept (always defined there). */
|
|
30
|
+
principal?: string;
|
|
31
|
+
/** When this connection was accepted (`Date.now()`). */
|
|
38
32
|
readonly connectedAt: number;
|
|
39
33
|
/** When the server last sent a heartbeat ping to this connection (managed by the server). */
|
|
40
34
|
lastPingAt?: number;
|
|
@@ -43,26 +37,33 @@ declare class Conn<Ev = Record<string, ServerMessageDef>, Ctx = unknown, Role ex
|
|
|
43
37
|
/** Pings sent since the last pong; drives reaping (managed by the server). */
|
|
44
38
|
missedPongs: number;
|
|
45
39
|
constructor(
|
|
46
|
-
/** The underlying
|
|
47
|
-
|
|
40
|
+
/** The underlying transport connection. `conn.terminate()` simulates a drop in tests. */
|
|
41
|
+
raw: RawConn,
|
|
48
42
|
/** Server-assigned unique id for this connection (stable for its lifetime). */
|
|
49
43
|
id: string,
|
|
50
44
|
/** This connection's role (the literal resolved by `authenticate`). */
|
|
51
45
|
role: Role,
|
|
52
46
|
/** The context `authenticate` returned for this connection. */
|
|
53
|
-
ctx: Ctx, serializer: Serializer,
|
|
47
|
+
ctx: Ctx, serializer: Serializer,
|
|
54
48
|
/** Optional inspector tap: called with each `emit` so the server can mirror it to inspectors. */
|
|
55
49
|
onEmit?: ((event: string, data: unknown) => void) | undefined);
|
|
56
|
-
private overBackpressure;
|
|
57
50
|
/** Encode and send a frame (unicast, e.g. req/res). */
|
|
58
51
|
send(frame: ServerFrame): void;
|
|
59
52
|
/** Forward an already-encoded frame (fan-out path; encoded once at the source). */
|
|
60
53
|
sendRaw(payload: string | Uint8Array): void;
|
|
61
54
|
/** Push an event to THIS connection (node-local). Scoped to the role's events. */
|
|
62
55
|
emit<E extends keyof Ev>(event: E, data: EmitData<Ev[E]>): void;
|
|
63
|
-
/**
|
|
56
|
+
/** Graceful close of the underlying transport connection. */
|
|
64
57
|
close(): void;
|
|
58
|
+
/** Hard close with no handshake — used by heartbeat reaping. */
|
|
59
|
+
terminate(): void;
|
|
65
60
|
}
|
|
61
|
+
/**
|
|
62
|
+
* The ACL principal for a connection: the stable `identify` key when configured, else the
|
|
63
|
+
* (random, per-connection) `conn.id`. Always returns a string — store access rules never key
|
|
64
|
+
* on `undefined`. Distinct from `identify`'s raw output (which may be undefined, used for presence).
|
|
65
|
+
*/
|
|
66
|
+
declare function resolvePrincipal(conn: Conn, identify?: (conn: Conn) => string | undefined): string;
|
|
66
67
|
|
|
67
68
|
/**
|
|
68
69
|
* In-process pub/sub bus. Share one bus across multiple servers to simulate
|
|
@@ -217,23 +218,42 @@ interface RoleLens<C extends Contract, R extends RoleOf<C>> {
|
|
|
217
218
|
/** Publish to a topic in role `R`'s surface (reaches that role's subscribers). */
|
|
218
219
|
publish<T extends keyof RoleTopics<C, R>>(topic: T, data: EmitData<RoleTopics<C, R>[T]>): void;
|
|
219
220
|
}
|
|
221
|
+
/**
|
|
222
|
+
* Server-side handle for one configured Store, reached via `srv.store.<name>`. The server is
|
|
223
|
+
* authoritative: it creates Resources, grants/revokes access, and may co-write. `data` is untyped
|
|
224
|
+
* (stores are off-contract — see ADR-0003); callers assert the shape.
|
|
225
|
+
*/
|
|
226
|
+
interface ServerStoreHandle {
|
|
227
|
+
/** Create a Resource with initial data + access rules (deny-by-default for everyone unlisted). */
|
|
228
|
+
create(id: string, data: unknown, accessRules: AccessRules): Promise<void>;
|
|
229
|
+
/** Read a Resource (data + accessRules), or undefined if absent. */
|
|
230
|
+
read(id: string): Promise<Resource | undefined>;
|
|
231
|
+
/** Server co-write: replace the Resource's value (LWW), fanned out to subscribers with a `server` origin. */
|
|
232
|
+
write(id: string, data: unknown): Promise<void>;
|
|
233
|
+
/** Grant a principal read/write on a Resource. */
|
|
234
|
+
grant(id: string, principal: string, perms: Perms): Promise<void>;
|
|
235
|
+
/** Revoke a principal's access to a Resource entirely. */
|
|
236
|
+
revoke(id: string, principal: string): Promise<void>;
|
|
237
|
+
/** Delete a Resource. */
|
|
238
|
+
delete(id: string): Promise<void>;
|
|
239
|
+
/** All Resource ids in this store. */
|
|
240
|
+
list(): Promise<string[]>;
|
|
241
|
+
}
|
|
220
242
|
/** Options for {@link createSuperLineServer}. */
|
|
221
243
|
interface SuperLineServerOptions<C extends Contract, A extends AuthResult<C>> {
|
|
222
|
-
/**
|
|
223
|
-
|
|
244
|
+
/** Client↔server transports to accept connections on (e.g. `webSocketServerTransport({ server })`). */
|
|
245
|
+
transports: ServerTransport[];
|
|
224
246
|
/** Wire serializer; MUST match the client. Defaults to `jsonSerializer`. */
|
|
225
247
|
serializer?: Serializer;
|
|
226
248
|
/** Cross-node fan-out adapter. Defaults to a per-server in-memory adapter. */
|
|
227
249
|
adapter?: Adapter;
|
|
228
|
-
/** Only handle upgrades for this pathname; others are left untouched. */
|
|
229
|
-
path?: string;
|
|
230
250
|
/**
|
|
231
251
|
* Friendly name for this node, surfaced in `srv.nodeName`, the cluster descriptor, and the
|
|
232
252
|
* Control Center topology. Defaults to `SUPER_LINE_NODE_NAME` or a short slice of `nodeId`.
|
|
233
253
|
*/
|
|
234
254
|
nodeName?: string;
|
|
235
|
-
/**
|
|
236
|
-
authenticate: (
|
|
255
|
+
/** Authenticate a connection from its normalized {@link Handshake}. Return { role, ctx }, or throw to reject. */
|
|
256
|
+
authenticate: (handshake: Handshake) => Awaitable<A>;
|
|
237
257
|
/** Stable user key for a connection (powers `cluster.byUser`, `isOnline`, and `toUser`). */
|
|
238
258
|
identify?: (conn: Conn) => string | undefined;
|
|
239
259
|
/** Extra fields merged into the connection's cluster descriptor (e.g. `{ plan }`). `ctx` is never auto-serialized. */
|
|
@@ -252,16 +272,20 @@ interface SuperLineServerOptions<C extends Contract, A extends AuthResult<C>> {
|
|
|
252
272
|
interval?: number;
|
|
253
273
|
maxMissed?: number;
|
|
254
274
|
} | false;
|
|
255
|
-
/** Guard against slow consumers: when a connection's send buffer exceeds the limit, close or drop. */
|
|
256
|
-
backpressure?: Backpressure;
|
|
257
275
|
/**
|
|
258
|
-
*
|
|
259
|
-
*
|
|
260
|
-
*
|
|
276
|
+
* Enable the read-only Control Center inspector: emit `msg.*` telemetry and accept inspector
|
|
277
|
+
* clients. The WS transport must also be created with `inspector: true` to negotiate the
|
|
278
|
+
* `superline.inspector.v1` subprotocol. **Default off; dev / trusted-network only.**
|
|
261
279
|
*/
|
|
262
280
|
inspector?: boolean | {
|
|
263
281
|
redact?: string[];
|
|
264
282
|
};
|
|
283
|
+
/**
|
|
284
|
+
* Pluggable persisted-state Stores, keyed by name (`{ scene: crdtStoreServer(), config: memoryStoreServer() }`).
|
|
285
|
+
* Each is the server half of a Store pair; the client passes the matching client halves. Surfaced as
|
|
286
|
+
* `srv.store.<name>` and `client.store.<name>`. Stores are off-contract and untyped (ADR-0003).
|
|
287
|
+
*/
|
|
288
|
+
stores?: Record<string, ServerStore>;
|
|
265
289
|
/** Called once per accepted connection. */
|
|
266
290
|
onConnection?: (conn: Conn, ctx: CtxUnion<A>) => void;
|
|
267
291
|
/** Called when a connection closes, with the WebSocket close `code`. */
|
|
@@ -300,6 +324,8 @@ interface SuperLineServer<C extends Contract, A extends AuthResult<C>> {
|
|
|
300
324
|
subscribe<T extends keyof SharedTopics<C>>(topic: T, handler: (data: EventData<SharedTopics<C>[T]>, meta: BusMeta) => void): () => void;
|
|
301
325
|
/** Lens for role-scoped sends, e.g. forRole('user').publish('feed', data). */
|
|
302
326
|
forRole<R extends RoleOf<C>>(role: R): RoleLens<C, R>;
|
|
327
|
+
/** Server-authoritative handle for a configured Store (`srv.store('scene').create(...)`). Throws `NOT_FOUND` if the name isn't configured. */
|
|
328
|
+
store(name: string): ServerStoreHandle;
|
|
303
329
|
close(): Promise<void>;
|
|
304
330
|
}
|
|
305
331
|
/**
|
|
@@ -315,8 +341,8 @@ interface SuperLineServer<C extends Contract, A extends AuthResult<C>> {
|
|
|
315
341
|
* @example
|
|
316
342
|
* ```ts
|
|
317
343
|
* const srv = createSuperLineServer(api, {
|
|
318
|
-
* server,
|
|
319
|
-
* authenticate: (
|
|
344
|
+
* transports: [webSocketServerTransport({ server })],
|
|
345
|
+
* authenticate: (h) => ({ role: 'user' as const, ctx: { id: '1' } }),
|
|
320
346
|
* })
|
|
321
347
|
* srv.implement({
|
|
322
348
|
* shared: { join: async ({ room }, _ctx, conn) => { srv.room(room).add(conn); return { ok: true } } },
|
|
@@ -326,4 +352,4 @@ interface SuperLineServer<C extends Contract, A extends AuthResult<C>> {
|
|
|
326
352
|
*/
|
|
327
353
|
declare function createSuperLineServer<C extends Contract, A extends AuthResult<C>>(contract: C, opts: SuperLineServerOptions<C, A>): SuperLineServer<C, A>;
|
|
328
354
|
|
|
329
|
-
export { type AuthResult, type
|
|
355
|
+
export { type AuthResult, type BusMeta, type ClusterView, Conn, type ConnTarget, type Handlers, type LocalView, MemoryBus, type Middleware, type MiddlewareInfo, type RoleLens, type Room, type ServerStoreHandle, type SuperLineServer, type SuperLineServerOptions, type UserTarget, createInMemoryAdapter, createSuperLineServer, resolvePrincipal };
|
package/dist/index.d.ts
CHANGED
|
@@ -1,25 +1,16 @@
|
|
|
1
|
-
import {
|
|
2
|
-
import { ServerMessageDef, Serializer, ServerFrame, EmitData, ConnDescriptor, Adapter, PresenceStore, Contract, RoleOf, NodeStat, SharedEvents, SharedServerRequests, ClientInput, Output, SharedRequests, ServerInput, AnyData, RoleRequests, Events, DataOf, RoleTopics, SharedTopics, EventData } from '@super-line/core';
|
|
3
|
-
import { WebSocket } from 'ws';
|
|
1
|
+
import { ServerMessageDef, RawConn, Serializer, ServerFrame, EmitData, ConnDescriptor, Adapter, PresenceStore, Contract, RoleOf, NodeStat, SharedEvents, SharedServerRequests, ClientInput, Output, SharedRequests, ServerInput, AnyData, RoleRequests, Events, DataOf, RoleTopics, AccessRules, Resource, Perms, SharedTopics, EventData, ServerTransport, Handshake, ServerStore } from '@super-line/core';
|
|
4
2
|
|
|
5
|
-
/** Backpressure policy: what to do when a connection's send buffer grows too large. */
|
|
6
|
-
interface Backpressure {
|
|
7
|
-
/** Buffer size (bytes) above which {@link Backpressure.onExceed} kicks in. */
|
|
8
|
-
maxBufferedBytes: number;
|
|
9
|
-
/** `'close'` (default) drops the connection with code 1013; `'drop'` skips the frame. */
|
|
10
|
-
onExceed?: 'close' | 'drop';
|
|
11
|
-
}
|
|
12
3
|
/**
|
|
13
4
|
* A single client connection, passed to handlers as the third argument.
|
|
14
5
|
*
|
|
15
|
-
* Node-local: `conn` objects live on the node that accepted the
|
|
6
|
+
* Node-local: `conn` objects live on the node that accepted the connection, so don't
|
|
16
7
|
* stash one to reach a user later — cross-node delivery goes through the Adapter
|
|
17
8
|
* (use a per-user room instead). Generic over the events it may emit (scoped by
|
|
18
9
|
* role), its `ctx`, and its `role`.
|
|
19
10
|
*/
|
|
20
11
|
declare class Conn<Ev = Record<string, ServerMessageDef>, Ctx = unknown, Role extends string = string, Data = unknown> {
|
|
21
|
-
/** The underlying
|
|
22
|
-
readonly
|
|
12
|
+
/** The underlying transport connection. `conn.terminate()` simulates a drop in tests. */
|
|
13
|
+
readonly raw: RawConn;
|
|
23
14
|
/** Server-assigned unique id for this connection (stable for its lifetime). */
|
|
24
15
|
readonly id: string;
|
|
25
16
|
/** This connection's role (the literal resolved by `authenticate`). */
|
|
@@ -27,14 +18,17 @@ declare class Conn<Ev = Record<string, ServerMessageDef>, Ctx = unknown, Role ex
|
|
|
27
18
|
/** The context `authenticate` returned for this connection. */
|
|
28
19
|
readonly ctx: Ctx;
|
|
29
20
|
private readonly serializer;
|
|
30
|
-
private readonly backpressure?;
|
|
31
21
|
/** Optional inspector tap: called with each `emit` so the server can mirror it to inspectors. */
|
|
32
22
|
private readonly onEmit?;
|
|
33
23
|
/** Namespaced channels (rooms + topics) this connection belongs to. */
|
|
34
24
|
readonly channels: Set<string>;
|
|
35
25
|
/** Mutable per-connection scratch state, typed per role by the contract's `data` schema. */
|
|
36
26
|
data: Data;
|
|
37
|
-
/**
|
|
27
|
+
/** The client↔server transport (wire) this connection was accepted on (set by the server at accept). */
|
|
28
|
+
transport?: string;
|
|
29
|
+
/** ACL identity for stores: `identify(conn) ?? conn.id`, set by the server at accept (always defined there). */
|
|
30
|
+
principal?: string;
|
|
31
|
+
/** When this connection was accepted (`Date.now()`). */
|
|
38
32
|
readonly connectedAt: number;
|
|
39
33
|
/** When the server last sent a heartbeat ping to this connection (managed by the server). */
|
|
40
34
|
lastPingAt?: number;
|
|
@@ -43,26 +37,33 @@ declare class Conn<Ev = Record<string, ServerMessageDef>, Ctx = unknown, Role ex
|
|
|
43
37
|
/** Pings sent since the last pong; drives reaping (managed by the server). */
|
|
44
38
|
missedPongs: number;
|
|
45
39
|
constructor(
|
|
46
|
-
/** The underlying
|
|
47
|
-
|
|
40
|
+
/** The underlying transport connection. `conn.terminate()` simulates a drop in tests. */
|
|
41
|
+
raw: RawConn,
|
|
48
42
|
/** Server-assigned unique id for this connection (stable for its lifetime). */
|
|
49
43
|
id: string,
|
|
50
44
|
/** This connection's role (the literal resolved by `authenticate`). */
|
|
51
45
|
role: Role,
|
|
52
46
|
/** The context `authenticate` returned for this connection. */
|
|
53
|
-
ctx: Ctx, serializer: Serializer,
|
|
47
|
+
ctx: Ctx, serializer: Serializer,
|
|
54
48
|
/** Optional inspector tap: called with each `emit` so the server can mirror it to inspectors. */
|
|
55
49
|
onEmit?: ((event: string, data: unknown) => void) | undefined);
|
|
56
|
-
private overBackpressure;
|
|
57
50
|
/** Encode and send a frame (unicast, e.g. req/res). */
|
|
58
51
|
send(frame: ServerFrame): void;
|
|
59
52
|
/** Forward an already-encoded frame (fan-out path; encoded once at the source). */
|
|
60
53
|
sendRaw(payload: string | Uint8Array): void;
|
|
61
54
|
/** Push an event to THIS connection (node-local). Scoped to the role's events. */
|
|
62
55
|
emit<E extends keyof Ev>(event: E, data: EmitData<Ev[E]>): void;
|
|
63
|
-
/**
|
|
56
|
+
/** Graceful close of the underlying transport connection. */
|
|
64
57
|
close(): void;
|
|
58
|
+
/** Hard close with no handshake — used by heartbeat reaping. */
|
|
59
|
+
terminate(): void;
|
|
65
60
|
}
|
|
61
|
+
/**
|
|
62
|
+
* The ACL principal for a connection: the stable `identify` key when configured, else the
|
|
63
|
+
* (random, per-connection) `conn.id`. Always returns a string — store access rules never key
|
|
64
|
+
* on `undefined`. Distinct from `identify`'s raw output (which may be undefined, used for presence).
|
|
65
|
+
*/
|
|
66
|
+
declare function resolvePrincipal(conn: Conn, identify?: (conn: Conn) => string | undefined): string;
|
|
66
67
|
|
|
67
68
|
/**
|
|
68
69
|
* In-process pub/sub bus. Share one bus across multiple servers to simulate
|
|
@@ -217,23 +218,42 @@ interface RoleLens<C extends Contract, R extends RoleOf<C>> {
|
|
|
217
218
|
/** Publish to a topic in role `R`'s surface (reaches that role's subscribers). */
|
|
218
219
|
publish<T extends keyof RoleTopics<C, R>>(topic: T, data: EmitData<RoleTopics<C, R>[T]>): void;
|
|
219
220
|
}
|
|
221
|
+
/**
|
|
222
|
+
* Server-side handle for one configured Store, reached via `srv.store.<name>`. The server is
|
|
223
|
+
* authoritative: it creates Resources, grants/revokes access, and may co-write. `data` is untyped
|
|
224
|
+
* (stores are off-contract — see ADR-0003); callers assert the shape.
|
|
225
|
+
*/
|
|
226
|
+
interface ServerStoreHandle {
|
|
227
|
+
/** Create a Resource with initial data + access rules (deny-by-default for everyone unlisted). */
|
|
228
|
+
create(id: string, data: unknown, accessRules: AccessRules): Promise<void>;
|
|
229
|
+
/** Read a Resource (data + accessRules), or undefined if absent. */
|
|
230
|
+
read(id: string): Promise<Resource | undefined>;
|
|
231
|
+
/** Server co-write: replace the Resource's value (LWW), fanned out to subscribers with a `server` origin. */
|
|
232
|
+
write(id: string, data: unknown): Promise<void>;
|
|
233
|
+
/** Grant a principal read/write on a Resource. */
|
|
234
|
+
grant(id: string, principal: string, perms: Perms): Promise<void>;
|
|
235
|
+
/** Revoke a principal's access to a Resource entirely. */
|
|
236
|
+
revoke(id: string, principal: string): Promise<void>;
|
|
237
|
+
/** Delete a Resource. */
|
|
238
|
+
delete(id: string): Promise<void>;
|
|
239
|
+
/** All Resource ids in this store. */
|
|
240
|
+
list(): Promise<string[]>;
|
|
241
|
+
}
|
|
220
242
|
/** Options for {@link createSuperLineServer}. */
|
|
221
243
|
interface SuperLineServerOptions<C extends Contract, A extends AuthResult<C>> {
|
|
222
|
-
/**
|
|
223
|
-
|
|
244
|
+
/** Client↔server transports to accept connections on (e.g. `webSocketServerTransport({ server })`). */
|
|
245
|
+
transports: ServerTransport[];
|
|
224
246
|
/** Wire serializer; MUST match the client. Defaults to `jsonSerializer`. */
|
|
225
247
|
serializer?: Serializer;
|
|
226
248
|
/** Cross-node fan-out adapter. Defaults to a per-server in-memory adapter. */
|
|
227
249
|
adapter?: Adapter;
|
|
228
|
-
/** Only handle upgrades for this pathname; others are left untouched. */
|
|
229
|
-
path?: string;
|
|
230
250
|
/**
|
|
231
251
|
* Friendly name for this node, surfaced in `srv.nodeName`, the cluster descriptor, and the
|
|
232
252
|
* Control Center topology. Defaults to `SUPER_LINE_NODE_NAME` or a short slice of `nodeId`.
|
|
233
253
|
*/
|
|
234
254
|
nodeName?: string;
|
|
235
|
-
/**
|
|
236
|
-
authenticate: (
|
|
255
|
+
/** Authenticate a connection from its normalized {@link Handshake}. Return { role, ctx }, or throw to reject. */
|
|
256
|
+
authenticate: (handshake: Handshake) => Awaitable<A>;
|
|
237
257
|
/** Stable user key for a connection (powers `cluster.byUser`, `isOnline`, and `toUser`). */
|
|
238
258
|
identify?: (conn: Conn) => string | undefined;
|
|
239
259
|
/** Extra fields merged into the connection's cluster descriptor (e.g. `{ plan }`). `ctx` is never auto-serialized. */
|
|
@@ -252,16 +272,20 @@ interface SuperLineServerOptions<C extends Contract, A extends AuthResult<C>> {
|
|
|
252
272
|
interval?: number;
|
|
253
273
|
maxMissed?: number;
|
|
254
274
|
} | false;
|
|
255
|
-
/** Guard against slow consumers: when a connection's send buffer exceeds the limit, close or drop. */
|
|
256
|
-
backpressure?: Backpressure;
|
|
257
275
|
/**
|
|
258
|
-
*
|
|
259
|
-
*
|
|
260
|
-
*
|
|
276
|
+
* Enable the read-only Control Center inspector: emit `msg.*` telemetry and accept inspector
|
|
277
|
+
* clients. The WS transport must also be created with `inspector: true` to negotiate the
|
|
278
|
+
* `superline.inspector.v1` subprotocol. **Default off; dev / trusted-network only.**
|
|
261
279
|
*/
|
|
262
280
|
inspector?: boolean | {
|
|
263
281
|
redact?: string[];
|
|
264
282
|
};
|
|
283
|
+
/**
|
|
284
|
+
* Pluggable persisted-state Stores, keyed by name (`{ scene: crdtStoreServer(), config: memoryStoreServer() }`).
|
|
285
|
+
* Each is the server half of a Store pair; the client passes the matching client halves. Surfaced as
|
|
286
|
+
* `srv.store.<name>` and `client.store.<name>`. Stores are off-contract and untyped (ADR-0003).
|
|
287
|
+
*/
|
|
288
|
+
stores?: Record<string, ServerStore>;
|
|
265
289
|
/** Called once per accepted connection. */
|
|
266
290
|
onConnection?: (conn: Conn, ctx: CtxUnion<A>) => void;
|
|
267
291
|
/** Called when a connection closes, with the WebSocket close `code`. */
|
|
@@ -300,6 +324,8 @@ interface SuperLineServer<C extends Contract, A extends AuthResult<C>> {
|
|
|
300
324
|
subscribe<T extends keyof SharedTopics<C>>(topic: T, handler: (data: EventData<SharedTopics<C>[T]>, meta: BusMeta) => void): () => void;
|
|
301
325
|
/** Lens for role-scoped sends, e.g. forRole('user').publish('feed', data). */
|
|
302
326
|
forRole<R extends RoleOf<C>>(role: R): RoleLens<C, R>;
|
|
327
|
+
/** Server-authoritative handle for a configured Store (`srv.store('scene').create(...)`). Throws `NOT_FOUND` if the name isn't configured. */
|
|
328
|
+
store(name: string): ServerStoreHandle;
|
|
303
329
|
close(): Promise<void>;
|
|
304
330
|
}
|
|
305
331
|
/**
|
|
@@ -315,8 +341,8 @@ interface SuperLineServer<C extends Contract, A extends AuthResult<C>> {
|
|
|
315
341
|
* @example
|
|
316
342
|
* ```ts
|
|
317
343
|
* const srv = createSuperLineServer(api, {
|
|
318
|
-
* server,
|
|
319
|
-
* authenticate: (
|
|
344
|
+
* transports: [webSocketServerTransport({ server })],
|
|
345
|
+
* authenticate: (h) => ({ role: 'user' as const, ctx: { id: '1' } }),
|
|
320
346
|
* })
|
|
321
347
|
* srv.implement({
|
|
322
348
|
* shared: { join: async ({ room }, _ctx, conn) => { srv.room(room).add(conn); return { ok: true } } },
|
|
@@ -326,4 +352,4 @@ interface SuperLineServer<C extends Contract, A extends AuthResult<C>> {
|
|
|
326
352
|
*/
|
|
327
353
|
declare function createSuperLineServer<C extends Contract, A extends AuthResult<C>>(contract: C, opts: SuperLineServerOptions<C, A>): SuperLineServer<C, A>;
|
|
328
354
|
|
|
329
|
-
export { type AuthResult, type
|
|
355
|
+
export { type AuthResult, type BusMeta, type ClusterView, Conn, type ConnTarget, type Handlers, type LocalView, MemoryBus, type Middleware, type MiddlewareInfo, type RoleLens, type Room, type ServerStoreHandle, type SuperLineServer, type SuperLineServerOptions, type UserTarget, createInMemoryAdapter, createSuperLineServer, resolvePrincipal };
|