@soulcraft/sdk 2.0.1 → 2.1.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/client/index.d.ts +5 -38
- package/dist/client/index.d.ts.map +1 -1
- package/dist/client/index.js +5 -47
- package/dist/client/index.js.map +1 -1
- package/dist/client/namespace-proxy.d.ts +3 -4
- package/dist/client/namespace-proxy.d.ts.map +1 -1
- package/dist/client/namespace-proxy.js +3 -4
- package/dist/client/namespace-proxy.js.map +1 -1
- package/dist/index.d.ts +6 -6
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +4 -4
- package/dist/index.js.map +1 -1
- package/dist/modules/hall/admin.d.ts +39 -0
- package/dist/modules/hall/admin.d.ts.map +1 -0
- package/dist/modules/hall/admin.js +115 -0
- package/dist/modules/hall/admin.js.map +1 -0
- package/dist/modules/hall/browser.d.ts +83 -27
- package/dist/modules/hall/browser.d.ts.map +1 -1
- package/dist/modules/hall/browser.js +238 -49
- package/dist/modules/hall/browser.js.map +1 -1
- package/dist/modules/hall/media.d.ts +164 -0
- package/dist/modules/hall/media.d.ts.map +1 -0
- package/dist/modules/hall/media.js +182 -0
- package/dist/modules/hall/media.js.map +1 -0
- package/dist/modules/hall/server.d.ts +119 -6
- package/dist/modules/hall/server.d.ts.map +1 -1
- package/dist/modules/hall/server.js +299 -9
- package/dist/modules/hall/server.js.map +1 -1
- package/dist/modules/hall/types.d.ts +705 -25
- package/dist/modules/hall/types.d.ts.map +1 -1
- package/dist/modules/hall/types.js +12 -7
- package/dist/modules/hall/types.js.map +1 -1
- package/dist/server/hall-handlers.d.ts +60 -14
- package/dist/server/hall-handlers.d.ts.map +1 -1
- package/dist/server/hall-handlers.js +61 -12
- package/dist/server/hall-handlers.js.map +1 -1
- package/dist/server/hono-router.d.ts +2 -9
- package/dist/server/hono-router.d.ts.map +1 -1
- package/dist/server/hono-router.js +2 -46
- package/dist/server/hono-router.js.map +1 -1
- package/dist/server/index.d.ts +4 -19
- package/dist/server/index.d.ts.map +1 -1
- package/dist/server/index.js +10 -29
- package/dist/server/index.js.map +1 -1
- package/dist/types.d.ts +2 -41
- package/dist/types.d.ts.map +1 -1
- package/docs/ADR-005-hall-integration.md +449 -0
- package/package.json +1 -1
- package/dist/client/create-client-sdk.d.ts +0 -113
- package/dist/client/create-client-sdk.d.ts.map +0 -1
- package/dist/client/create-client-sdk.js +0 -169
- package/dist/client/create-client-sdk.js.map +0 -1
- package/dist/modules/app-context/index.d.ts +0 -214
- package/dist/modules/app-context/index.d.ts.map +0 -1
- package/dist/modules/app-context/index.js +0 -569
- package/dist/modules/app-context/index.js.map +0 -1
- package/dist/modules/billing/firestore-provider.d.ts +0 -60
- package/dist/modules/billing/firestore-provider.d.ts.map +0 -1
- package/dist/modules/billing/firestore-provider.js +0 -315
- package/dist/modules/billing/firestore-provider.js.map +0 -1
- package/dist/modules/brainy/proxy.d.ts +0 -48
- package/dist/modules/brainy/proxy.d.ts.map +0 -1
- package/dist/modules/brainy/proxy.js +0 -95
- package/dist/modules/brainy/proxy.js.map +0 -1
- package/dist/server/create-sdk.d.ts +0 -74
- package/dist/server/create-sdk.d.ts.map +0 -1
- package/dist/server/create-sdk.js +0 -104
- package/dist/server/create-sdk.js.map +0 -1
- package/dist/server/from-license.d.ts +0 -252
- package/dist/server/from-license.d.ts.map +0 -1
- package/dist/server/from-license.js +0 -349
- package/dist/server/from-license.js.map +0 -1
- package/dist/server/handlers.d.ts +0 -312
- package/dist/server/handlers.d.ts.map +0 -1
- package/dist/server/handlers.js +0 -376
- package/dist/server/handlers.js.map +0 -1
- package/dist/server/postmessage-handler.d.ts +0 -152
- package/dist/server/postmessage-handler.d.ts.map +0 -1
- package/dist/server/postmessage-handler.js +0 -138
- package/dist/server/postmessage-handler.js.map +0 -1
- package/dist/transports/http.d.ts +0 -86
- package/dist/transports/http.d.ts.map +0 -1
- package/dist/transports/http.js +0 -137
- package/dist/transports/http.js.map +0 -1
- package/dist/transports/postmessage.d.ts +0 -159
- package/dist/transports/postmessage.d.ts.map +0 -1
- package/dist/transports/postmessage.js +0 -207
- package/dist/transports/postmessage.js.map +0 -1
- package/dist/transports/workshop.d.ts +0 -173
- package/dist/transports/workshop.d.ts.map +0 -1
- package/dist/transports/workshop.js +0 -307
- package/dist/transports/workshop.js.map +0 -1
|
@@ -2,20 +2,25 @@
|
|
|
2
2
|
* @module modules/hall/types
|
|
3
3
|
* @description Types for the sdk.hall namespace — the Soulcraft real-time communication layer.
|
|
4
4
|
*
|
|
5
|
-
* Hall is a standalone Rust server at `hall.soulcraft.com`. It
|
|
6
|
-
*
|
|
7
|
-
*
|
|
5
|
+
* Hall is a standalone Rust server at `hall.soulcraft.com`. It provides:
|
|
6
|
+
* - **WebRTC SFU** — video/audio/data channels with RFC 6464 speaker detection
|
|
7
|
+
* - **Whisper ASR** — in-process transcription with BERT concept matching
|
|
8
|
+
* - **Pub/Sub** — product-scoped topic routing with presence tracking and replay buffers
|
|
9
|
+
* - **Media Pipeline** — upload, ffmpeg transcoding, thumbnails, async notifications
|
|
10
|
+
* - **Broadcast** — three-tier auto-scaling: participant (SFU) → WHEP → LL-HLS
|
|
11
|
+
* - **Recording** — per-track MKV + optional composite MP4 with webhook notifications
|
|
8
12
|
*
|
|
9
13
|
* The `sdk.hall` namespace has two faces depending on context:
|
|
10
14
|
*
|
|
11
15
|
* **Server mode** (`@soulcraft/sdk/server`) — the product backend (Academy, Workshop, Venue)
|
|
12
|
-
* connects to Hall with a shared secret, creates rooms,
|
|
13
|
-
* Use `createHallModule(options)` from `@soulcraft/sdk/server`.
|
|
16
|
+
* connects to Hall with a shared secret, creates rooms, manages pub/sub topics, uploads
|
|
17
|
+
* media, and mints session tokens. Use `createHallModule(options)` from `@soulcraft/sdk/server`.
|
|
14
18
|
*
|
|
15
19
|
* **Client mode** (`@soulcraft/sdk/client`) — a browser kit app joins a room using a session
|
|
16
|
-
* token
|
|
20
|
+
* token, or connects to pub/sub using a pubsub token. Use `joinHallRoom()` or
|
|
21
|
+
* `joinHallPubsub()` from `@soulcraft/sdk/client`.
|
|
17
22
|
*
|
|
18
|
-
* Products never pass their shared secret to the browser.
|
|
23
|
+
* Products never pass their shared secret to the browser. Tokens are the only browser credentials.
|
|
19
24
|
*/
|
|
20
25
|
/**
|
|
21
26
|
* Options for the server-side Hall connection (product backend → Hall server).
|
|
@@ -54,7 +59,28 @@ export interface ConceptInput {
|
|
|
54
59
|
/** Kit-defined subtype, if any. */
|
|
55
60
|
subtype?: string;
|
|
56
61
|
}
|
|
57
|
-
/**
|
|
62
|
+
/**
|
|
63
|
+
* Options for creating a room. All fields are optional with safe defaults.
|
|
64
|
+
*
|
|
65
|
+
* @example Standard room
|
|
66
|
+
* ```typescript
|
|
67
|
+
* await hall.createRoom('cohort-123', {
|
|
68
|
+
* enableTranscription: true,
|
|
69
|
+
* concepts: await loadConcepts(cohortId),
|
|
70
|
+
* })
|
|
71
|
+
* ```
|
|
72
|
+
*
|
|
73
|
+
* @example Broadcast room (webinar/lecture)
|
|
74
|
+
* ```typescript
|
|
75
|
+
* await hall.createRoom('lecture-456', {
|
|
76
|
+
* maxParticipants: 5,
|
|
77
|
+
* allowBroadcast: true,
|
|
78
|
+
* enableRecording: true,
|
|
79
|
+
* recordingComposite: true,
|
|
80
|
+
* recordingWebhookUrl: 'https://academy.soulcraft.com/api/recordings/complete',
|
|
81
|
+
* })
|
|
82
|
+
* ```
|
|
83
|
+
*/
|
|
58
84
|
export interface RoomOptions {
|
|
59
85
|
/** Maximum peers before the SFU stops accepting new connections (default: 30). */
|
|
60
86
|
maxPeers?: number;
|
|
@@ -72,6 +98,49 @@ export interface RoomOptions {
|
|
|
72
98
|
* Pass an empty array to disable concept matching while keeping transcription.
|
|
73
99
|
*/
|
|
74
100
|
concepts?: ConceptInput[];
|
|
101
|
+
/**
|
|
102
|
+
* Maximum bidirectional participants in broadcast rooms (default: 30).
|
|
103
|
+
* Overflow joiners are assigned as viewers (WHEP or LL-HLS).
|
|
104
|
+
* Only meaningful when `allowBroadcast` is true.
|
|
105
|
+
*/
|
|
106
|
+
maxParticipants?: number;
|
|
107
|
+
/**
|
|
108
|
+
* Enable auto-scaling broadcast. When true, overflow joiners become viewers
|
|
109
|
+
* using WHEP (sub-second latency) or LL-HLS (2–3s, unlimited scale).
|
|
110
|
+
* Default: false.
|
|
111
|
+
*/
|
|
112
|
+
allowBroadcast?: boolean;
|
|
113
|
+
/**
|
|
114
|
+
* Produce a composite MP4 recording (mixed audio + active speaker video)
|
|
115
|
+
* in addition to individual per-track MKV files.
|
|
116
|
+
* Default: false.
|
|
117
|
+
*/
|
|
118
|
+
recordingComposite?: boolean;
|
|
119
|
+
/**
|
|
120
|
+
* Webhook URL for recording completion notification.
|
|
121
|
+
* Hall POSTs `{ sessionId, manifest }` JSON when recording finishes.
|
|
122
|
+
* Retried with exponential backoff (3 attempts).
|
|
123
|
+
*/
|
|
124
|
+
recordingWebhookUrl?: string;
|
|
125
|
+
/**
|
|
126
|
+
* Minimum peer count before selective transcription kicks in.
|
|
127
|
+
* When the room has at least this many peers, only the active speaker's audio
|
|
128
|
+
* is sent to Whisper (saves CPU). Set to 0 or omit to transcribe all peers.
|
|
129
|
+
* Default: 0 (disabled).
|
|
130
|
+
*/
|
|
131
|
+
selectiveTranscriptionThreshold?: number;
|
|
132
|
+
/**
|
|
133
|
+
* Webhook URL for room event delivery via HTTP POST.
|
|
134
|
+
* Hall POSTs JSON events with `X-Hall-Signature: hmac-sha256(body, product_secret)`
|
|
135
|
+
* header for verification. Retried with exponential backoff (3 attempts).
|
|
136
|
+
*/
|
|
137
|
+
webhookUrl?: string;
|
|
138
|
+
/**
|
|
139
|
+
* Filter which events are delivered to `webhookUrl`.
|
|
140
|
+
* e.g. `["peerJoined", "peerLeft", "transcript", "roomClosed"]`.
|
|
141
|
+
* If omitted, all room events are delivered.
|
|
142
|
+
*/
|
|
143
|
+
webhookEvents?: string[];
|
|
75
144
|
}
|
|
76
145
|
/**
|
|
77
146
|
* Manifest emitted when recording stops.
|
|
@@ -89,6 +158,16 @@ export interface RecordingManifest {
|
|
|
89
158
|
audioTracks: string[];
|
|
90
159
|
/** Absolute paths to per-participant video track MKV files. */
|
|
91
160
|
videoTracks: string[];
|
|
161
|
+
/**
|
|
162
|
+
* Path to the composite MP4 (mixed audio + active speaker video).
|
|
163
|
+
* Only present when `RoomOptions.recordingComposite` was true.
|
|
164
|
+
*/
|
|
165
|
+
compositePath?: string;
|
|
166
|
+
/**
|
|
167
|
+
* Cloud storage URLs (if cloud upload was configured in `hall.toml`).
|
|
168
|
+
* Populated after upload completes (GCS, S3, etc.).
|
|
169
|
+
*/
|
|
170
|
+
cloudUrls?: string[];
|
|
92
171
|
}
|
|
93
172
|
/** A Whisper ASR transcript segment from a peer in the room. */
|
|
94
173
|
export interface TranscriptEvent {
|
|
@@ -146,6 +225,272 @@ export interface PeerLeftEvent {
|
|
|
146
225
|
roomId: string;
|
|
147
226
|
peerId: string;
|
|
148
227
|
}
|
|
228
|
+
/** Confirmation that a session token was revoked. */
|
|
229
|
+
export interface TokenRevokedEvent {
|
|
230
|
+
/** Hash of the revoked token (for log correlation, not the token itself). */
|
|
231
|
+
tokenHash: string;
|
|
232
|
+
}
|
|
233
|
+
/** Result of creating a WHIP ingest session. */
|
|
234
|
+
export interface WhipSession {
|
|
235
|
+
/** Resource ID for trickle ICE and teardown (URL path suffix). */
|
|
236
|
+
resourceId: string;
|
|
237
|
+
/** SDP answer to pass back to the WHIP publisher (OBS, FFmpeg, etc.). */
|
|
238
|
+
answerSdp: string;
|
|
239
|
+
/** Room ID this session publishes into. */
|
|
240
|
+
roomId: string;
|
|
241
|
+
/** Peer ID assigned to this publisher in the room. */
|
|
242
|
+
peerId: string;
|
|
243
|
+
}
|
|
244
|
+
/** Options for creating a Hall admin client. */
|
|
245
|
+
export interface HallAdminOptions {
|
|
246
|
+
/** Base HTTP URL of the Hall server (e.g. `"https://hall.soulcraft.com"`). */
|
|
247
|
+
hallUrl: string;
|
|
248
|
+
/** Admin secret from `[admin].secret` in `hall.toml`. */
|
|
249
|
+
secret: string;
|
|
250
|
+
}
|
|
251
|
+
/** A room entry from the admin room list. */
|
|
252
|
+
export interface AdminRoom {
|
|
253
|
+
roomId: string;
|
|
254
|
+
sessionId: string;
|
|
255
|
+
}
|
|
256
|
+
/** A connected product from the admin products list. */
|
|
257
|
+
export interface AdminProduct {
|
|
258
|
+
name: string;
|
|
259
|
+
connections: number;
|
|
260
|
+
}
|
|
261
|
+
/** An audit log entry from the admin audit endpoint. */
|
|
262
|
+
export interface AuditEntry {
|
|
263
|
+
timestampMs: number;
|
|
264
|
+
eventType: string;
|
|
265
|
+
[key: string]: unknown;
|
|
266
|
+
}
|
|
267
|
+
/**
|
|
268
|
+
* REST client for the Hall `/admin` API.
|
|
269
|
+
*
|
|
270
|
+
* Separate from `HallModule` — uses HTTP with `Authorization: Bearer {admin_secret}`
|
|
271
|
+
* rather than the product WebSocket connection. Create via `createHallAdmin()`.
|
|
272
|
+
*
|
|
273
|
+
* @example
|
|
274
|
+
* ```typescript
|
|
275
|
+
* import { createHallAdmin } from '@soulcraft/sdk/server'
|
|
276
|
+
*
|
|
277
|
+
* const admin = createHallAdmin({
|
|
278
|
+
* hallUrl: 'https://hall.soulcraft.com',
|
|
279
|
+
* secret: process.env.HALL_ADMIN_SECRET!,
|
|
280
|
+
* })
|
|
281
|
+
* const { rooms } = await admin.listRooms()
|
|
282
|
+
* await admin.kickPeer('room-1', 'disruptive-user')
|
|
283
|
+
* ```
|
|
284
|
+
*/
|
|
285
|
+
export interface HallAdminClient {
|
|
286
|
+
/** List all active rooms with session IDs. */
|
|
287
|
+
listRooms(): Promise<{
|
|
288
|
+
rooms: AdminRoom[];
|
|
289
|
+
count: number;
|
|
290
|
+
}>;
|
|
291
|
+
/** Get detail for a single room. */
|
|
292
|
+
getRoom(roomId: string): Promise<AdminRoom>;
|
|
293
|
+
/** Force-close a room and disconnect all peers. */
|
|
294
|
+
closeRoom(roomId: string): Promise<void>;
|
|
295
|
+
/** Kick a peer from a room. */
|
|
296
|
+
kickPeer(roomId: string, peerId: string): Promise<void>;
|
|
297
|
+
/** Revoke a session token. */
|
|
298
|
+
revokeToken(token: string): Promise<void>;
|
|
299
|
+
/** List connected products with connection counts. */
|
|
300
|
+
listProducts(): Promise<{
|
|
301
|
+
products: AdminProduct[];
|
|
302
|
+
}>;
|
|
303
|
+
/** List pub/sub topics with subscriber counts. */
|
|
304
|
+
listTopics(): Promise<{
|
|
305
|
+
topicCount: number;
|
|
306
|
+
totalSubscribers: number;
|
|
307
|
+
totalMessages: number;
|
|
308
|
+
}>;
|
|
309
|
+
/** Get current config (secrets redacted). */
|
|
310
|
+
getConfig(): Promise<Record<string, unknown>>;
|
|
311
|
+
/** Trigger a config reload (equivalent to SIGHUP). */
|
|
312
|
+
reloadConfig(): Promise<{
|
|
313
|
+
status: string;
|
|
314
|
+
}>;
|
|
315
|
+
/** Get recent audit log entries. */
|
|
316
|
+
getAuditLog(limit?: number): Promise<{
|
|
317
|
+
entries: AuditEntry[];
|
|
318
|
+
count: number;
|
|
319
|
+
}>;
|
|
320
|
+
}
|
|
321
|
+
/** A single replayed message from a topic's replay buffer. */
|
|
322
|
+
export interface ReplayEntry {
|
|
323
|
+
/** Sender identifier (product name or peer ID). */
|
|
324
|
+
senderId: string;
|
|
325
|
+
/** The message payload. */
|
|
326
|
+
payload: unknown;
|
|
327
|
+
/** Unix timestamp in milliseconds when the message was originally sent. */
|
|
328
|
+
timestampMs: number;
|
|
329
|
+
}
|
|
330
|
+
/** Confirmation of a pub/sub topic subscription. */
|
|
331
|
+
export interface TopicSubscribedEvent {
|
|
332
|
+
/** Topic that was subscribed to. */
|
|
333
|
+
topic: string;
|
|
334
|
+
/** Current number of subscribers on this topic. */
|
|
335
|
+
subscriberCount: number;
|
|
336
|
+
/** Replay buffer entries (most recent messages, if any). */
|
|
337
|
+
replay: ReplayEntry[];
|
|
338
|
+
}
|
|
339
|
+
/** Confirmation of a pub/sub topic unsubscription. */
|
|
340
|
+
export interface TopicUnsubscribedEvent {
|
|
341
|
+
/** Topic that was unsubscribed from. */
|
|
342
|
+
topic: string;
|
|
343
|
+
}
|
|
344
|
+
/** A message received on a subscribed pub/sub topic. */
|
|
345
|
+
export interface TopicMessageEvent {
|
|
346
|
+
/** Topic the message was published to. */
|
|
347
|
+
topic: string;
|
|
348
|
+
/** Sender identifier (product name or peer ID). */
|
|
349
|
+
senderId: string;
|
|
350
|
+
/** The message payload. */
|
|
351
|
+
payload: unknown;
|
|
352
|
+
/** Unix timestamp in milliseconds. */
|
|
353
|
+
timestampMs: number;
|
|
354
|
+
}
|
|
355
|
+
/** A subscriber joined or left a pub/sub topic. */
|
|
356
|
+
export interface PresenceUpdateEvent {
|
|
357
|
+
/** Topic the presence event occurred on. */
|
|
358
|
+
topic: string;
|
|
359
|
+
/** Peer who joined or left. */
|
|
360
|
+
peerId: string;
|
|
361
|
+
/** Whether the peer joined or left. */
|
|
362
|
+
action: 'joined' | 'left';
|
|
363
|
+
/** Presence metadata (only present on join). */
|
|
364
|
+
metadata?: Record<string, unknown>;
|
|
365
|
+
/** Current subscriber count after this event. */
|
|
366
|
+
subscriberCount: number;
|
|
367
|
+
}
|
|
368
|
+
/** A single subscriber in a presence snapshot. */
|
|
369
|
+
export interface PresenceEntry {
|
|
370
|
+
/** Subscriber identifier. */
|
|
371
|
+
peerId: string;
|
|
372
|
+
/** Optional presence metadata set at subscribe time. */
|
|
373
|
+
metadata?: Record<string, unknown>;
|
|
374
|
+
}
|
|
375
|
+
/** Full snapshot of all current subscribers on a topic. */
|
|
376
|
+
export interface PresenceSnapshotEvent {
|
|
377
|
+
/** Topic queried. */
|
|
378
|
+
topic: string;
|
|
379
|
+
/** All current subscribers with their metadata. */
|
|
380
|
+
subscribers: PresenceEntry[];
|
|
381
|
+
}
|
|
382
|
+
/** Metadata for a processed media file. */
|
|
383
|
+
export interface MediaInfo {
|
|
384
|
+
/** Unique media identifier. */
|
|
385
|
+
mediaId: string;
|
|
386
|
+
/** Product that uploaded this media. */
|
|
387
|
+
productName: string;
|
|
388
|
+
/** Original filename from the upload. */
|
|
389
|
+
originalFilename: string;
|
|
390
|
+
/** MIME type of the original file. */
|
|
391
|
+
mimeType: string;
|
|
392
|
+
/** File size in bytes. */
|
|
393
|
+
size: number;
|
|
394
|
+
/** Duration in seconds (audio/video only). */
|
|
395
|
+
duration?: number;
|
|
396
|
+
/** Width × height in pixels (image/video only). */
|
|
397
|
+
dimensions?: [number, number];
|
|
398
|
+
/** Whether the file was transcoded from its original format. */
|
|
399
|
+
transcoded: boolean;
|
|
400
|
+
/** Processing status. */
|
|
401
|
+
status: 'processing' | 'ready' | 'error';
|
|
402
|
+
/** ISO 8601 timestamp when the media was uploaded. */
|
|
403
|
+
createdAt: string;
|
|
404
|
+
}
|
|
405
|
+
/** Notification that a media file has been processed and is ready. */
|
|
406
|
+
export interface MediaReadyEvent {
|
|
407
|
+
/** Unique media identifier. */
|
|
408
|
+
mediaId: string;
|
|
409
|
+
/** MIME type of the original file. */
|
|
410
|
+
mimeType: string;
|
|
411
|
+
/** File size in bytes. */
|
|
412
|
+
size: number;
|
|
413
|
+
/** Duration in seconds (audio/video only). */
|
|
414
|
+
duration?: number;
|
|
415
|
+
/** Width × height in pixels (image/video only). */
|
|
416
|
+
dimensions?: [number, number];
|
|
417
|
+
}
|
|
418
|
+
/** Notification that media processing failed. */
|
|
419
|
+
export interface MediaErrorEvent {
|
|
420
|
+
/** Media identifier that failed. */
|
|
421
|
+
mediaId: string;
|
|
422
|
+
/** Human-readable error description. */
|
|
423
|
+
error: string;
|
|
424
|
+
}
|
|
425
|
+
/**
|
|
426
|
+
* Supported transcode target formats for media uploads.
|
|
427
|
+
* Sent as the `transcode` field in the upload form.
|
|
428
|
+
*/
|
|
429
|
+
export type TranscodeTarget = 'audio/mp3' | 'video/mp4' | 'image/webp';
|
|
430
|
+
/**
|
|
431
|
+
* Role assigned to a peer in a broadcast room.
|
|
432
|
+
* - `participant` — full bidirectional WebRTC (can publish and receive media)
|
|
433
|
+
* - `viewer` — receive-only (WHEP or LL-HLS, cannot publish)
|
|
434
|
+
*/
|
|
435
|
+
export type HallPeerRole = 'participant' | 'viewer';
|
|
436
|
+
/** A viewer was promoted to full participant. */
|
|
437
|
+
export interface PeerPromotedEvent {
|
|
438
|
+
roomId: string;
|
|
439
|
+
peerId: string;
|
|
440
|
+
}
|
|
441
|
+
/** A participant was demoted to viewer. */
|
|
442
|
+
export interface PeerDemotedEvent {
|
|
443
|
+
roomId: string;
|
|
444
|
+
peerId: string;
|
|
445
|
+
}
|
|
446
|
+
/** Periodic viewer count update for broadcast rooms (every ~5s). */
|
|
447
|
+
export interface ViewerCountEvent {
|
|
448
|
+
roomId: string;
|
|
449
|
+
/** Number of full bidirectional participants. */
|
|
450
|
+
participants: number;
|
|
451
|
+
/** Number of WHEP (sub-second latency) viewers. */
|
|
452
|
+
whepViewers: number;
|
|
453
|
+
/** Number of LL-HLS viewers. */
|
|
454
|
+
hlsViewers: number;
|
|
455
|
+
}
|
|
456
|
+
/**
|
|
457
|
+
* Screen share simulcast mode.
|
|
458
|
+
* - `static` — prioritize resolution (presentations, documents)
|
|
459
|
+
* - `motion` — prioritize framerate (demos, video playback)
|
|
460
|
+
*/
|
|
461
|
+
export type ScreenShareMode = 'static' | 'motion';
|
|
462
|
+
/** Periodic thumbnail of an active screen share. */
|
|
463
|
+
export interface ScreenShareThumbnailEvent {
|
|
464
|
+
roomId: string;
|
|
465
|
+
peerId: string;
|
|
466
|
+
/** URL to the latest thumbnail image. */
|
|
467
|
+
thumbnailUrl: string;
|
|
468
|
+
}
|
|
469
|
+
/** A single chat message in room history. */
|
|
470
|
+
export interface ChatMessage {
|
|
471
|
+
/** Peer who sent the message. */
|
|
472
|
+
peerId: string;
|
|
473
|
+
/** Message text. */
|
|
474
|
+
text: string;
|
|
475
|
+
/** Unix timestamp in milliseconds. */
|
|
476
|
+
timestampMs: number;
|
|
477
|
+
}
|
|
478
|
+
/** Response to a `getChatHistory` request. */
|
|
479
|
+
export interface ChatHistoryEvent {
|
|
480
|
+
roomId: string;
|
|
481
|
+
/** Chat messages in chronological order. */
|
|
482
|
+
messages: ChatMessage[];
|
|
483
|
+
}
|
|
484
|
+
/**
|
|
485
|
+
* Sent to a browser client when their role changes (promoted or demoted).
|
|
486
|
+
* The SDK handles transport upgrade/downgrade transparently.
|
|
487
|
+
*/
|
|
488
|
+
export interface RoleChangedEvent {
|
|
489
|
+
/** New role: `"participant"` or `"viewer"`. */
|
|
490
|
+
role: HallPeerRole;
|
|
491
|
+
/** Whether the peer can now publish media tracks. */
|
|
492
|
+
canPublish: boolean;
|
|
493
|
+
}
|
|
149
494
|
/**
|
|
150
495
|
* Messages sent from the product backend to the Hall server over the product WebSocket.
|
|
151
496
|
* Encoded as msgpack with `t` (type tag) and `d` (data) fields — matching Rust serde
|
|
@@ -179,6 +524,7 @@ export type HallClientMessage = {
|
|
|
179
524
|
roomId: string;
|
|
180
525
|
peerId: string;
|
|
181
526
|
ttlSecs?: number | undefined;
|
|
527
|
+
role?: HallPeerRole | undefined;
|
|
182
528
|
};
|
|
183
529
|
} | {
|
|
184
530
|
t: 'startRecording';
|
|
@@ -197,6 +543,65 @@ export type HallClientMessage = {
|
|
|
197
543
|
channel: string;
|
|
198
544
|
payload: Uint8Array;
|
|
199
545
|
};
|
|
546
|
+
} | {
|
|
547
|
+
t: 'subscribeTopic';
|
|
548
|
+
d: {
|
|
549
|
+
topic: string;
|
|
550
|
+
metadata?: Record<string, unknown> | undefined;
|
|
551
|
+
};
|
|
552
|
+
} | {
|
|
553
|
+
t: 'unsubscribeTopic';
|
|
554
|
+
d: {
|
|
555
|
+
topic: string;
|
|
556
|
+
};
|
|
557
|
+
} | {
|
|
558
|
+
t: 'broadcastTopic';
|
|
559
|
+
d: {
|
|
560
|
+
topic: string;
|
|
561
|
+
payload: unknown;
|
|
562
|
+
};
|
|
563
|
+
} | {
|
|
564
|
+
t: 'getPresence';
|
|
565
|
+
d: {
|
|
566
|
+
topic: string;
|
|
567
|
+
};
|
|
568
|
+
} | {
|
|
569
|
+
t: 'createPubsubToken';
|
|
570
|
+
d: {
|
|
571
|
+
peerId: string;
|
|
572
|
+
allowedTopics?: string[] | undefined;
|
|
573
|
+
ttlSecs?: number | undefined;
|
|
574
|
+
};
|
|
575
|
+
} | {
|
|
576
|
+
t: 'promotePeer';
|
|
577
|
+
d: {
|
|
578
|
+
roomId: string;
|
|
579
|
+
peerId: string;
|
|
580
|
+
};
|
|
581
|
+
} | {
|
|
582
|
+
t: 'demotePeer';
|
|
583
|
+
d: {
|
|
584
|
+
roomId: string;
|
|
585
|
+
peerId: string;
|
|
586
|
+
};
|
|
587
|
+
} | {
|
|
588
|
+
t: 'setScreenShareMode';
|
|
589
|
+
d: {
|
|
590
|
+
roomId: string;
|
|
591
|
+
peerId: string;
|
|
592
|
+
mode: ScreenShareMode;
|
|
593
|
+
};
|
|
594
|
+
} | {
|
|
595
|
+
t: 'getChatHistory';
|
|
596
|
+
d: {
|
|
597
|
+
roomId: string;
|
|
598
|
+
lastN?: number | undefined;
|
|
599
|
+
};
|
|
600
|
+
} | {
|
|
601
|
+
t: 'revokeToken';
|
|
602
|
+
d: {
|
|
603
|
+
token: string;
|
|
604
|
+
};
|
|
200
605
|
};
|
|
201
606
|
/**
|
|
202
607
|
* Messages sent from the Hall server to the product backend.
|
|
@@ -276,6 +681,51 @@ export type HallServerMessage = {
|
|
|
276
681
|
} | {
|
|
277
682
|
t: 'peerLeft';
|
|
278
683
|
d: PeerLeftEvent;
|
|
684
|
+
} | {
|
|
685
|
+
t: 'topicSubscribed';
|
|
686
|
+
d: TopicSubscribedEvent;
|
|
687
|
+
} | {
|
|
688
|
+
t: 'topicUnsubscribed';
|
|
689
|
+
d: TopicUnsubscribedEvent;
|
|
690
|
+
} | {
|
|
691
|
+
t: 'topicMessage';
|
|
692
|
+
d: TopicMessageEvent;
|
|
693
|
+
} | {
|
|
694
|
+
t: 'presenceUpdate';
|
|
695
|
+
d: PresenceUpdateEvent;
|
|
696
|
+
} | {
|
|
697
|
+
t: 'presenceSnapshot';
|
|
698
|
+
d: PresenceSnapshotEvent;
|
|
699
|
+
} | {
|
|
700
|
+
t: 'pubsubToken';
|
|
701
|
+
d: {
|
|
702
|
+
token: string;
|
|
703
|
+
expiresAt: number;
|
|
704
|
+
};
|
|
705
|
+
} | {
|
|
706
|
+
t: 'mediaReady';
|
|
707
|
+
d: MediaReadyEvent;
|
|
708
|
+
} | {
|
|
709
|
+
t: 'mediaError';
|
|
710
|
+
d: MediaErrorEvent;
|
|
711
|
+
} | {
|
|
712
|
+
t: 'peerPromoted';
|
|
713
|
+
d: PeerPromotedEvent;
|
|
714
|
+
} | {
|
|
715
|
+
t: 'peerDemoted';
|
|
716
|
+
d: PeerDemotedEvent;
|
|
717
|
+
} | {
|
|
718
|
+
t: 'viewerCount';
|
|
719
|
+
d: ViewerCountEvent;
|
|
720
|
+
} | {
|
|
721
|
+
t: 'screenShareThumbnail';
|
|
722
|
+
d: ScreenShareThumbnailEvent;
|
|
723
|
+
} | {
|
|
724
|
+
t: 'chatHistory';
|
|
725
|
+
d: ChatHistoryEvent;
|
|
726
|
+
} | {
|
|
727
|
+
t: 'tokenRevoked';
|
|
728
|
+
d: TokenRevokedEvent;
|
|
279
729
|
};
|
|
280
730
|
/** Event map for server-side {@link HallRoom} listeners. */
|
|
281
731
|
export interface HallRoomEvents {
|
|
@@ -286,6 +736,13 @@ export interface HallRoomEvents {
|
|
|
286
736
|
peerJoined: PeerJoinedEvent;
|
|
287
737
|
peerLeft: PeerLeftEvent;
|
|
288
738
|
recordingManifest: RecordingManifest;
|
|
739
|
+
peerPromoted: PeerPromotedEvent;
|
|
740
|
+
peerDemoted: PeerDemotedEvent;
|
|
741
|
+
viewerCount: ViewerCountEvent;
|
|
742
|
+
screenShareThumbnail: ScreenShareThumbnailEvent;
|
|
743
|
+
chatHistory: ChatHistoryEvent;
|
|
744
|
+
mediaReady: MediaReadyEvent;
|
|
745
|
+
mediaError: MediaErrorEvent;
|
|
289
746
|
closed: {
|
|
290
747
|
roomId: string;
|
|
291
748
|
};
|
|
@@ -293,9 +750,9 @@ export interface HallRoomEvents {
|
|
|
293
750
|
/**
|
|
294
751
|
* The `sdk.hall` namespace on `SoulcraftSDK` in **server mode**.
|
|
295
752
|
*
|
|
296
|
-
* Products call these methods in request handlers to create rooms
|
|
297
|
-
*
|
|
298
|
-
* the product only holds a control-plane connection over WebSocket.
|
|
753
|
+
* Products call these methods in request handlers to create rooms, manage pub/sub
|
|
754
|
+
* topics, upload media, and issue session tokens. Rooms and topics are managed by
|
|
755
|
+
* the Hall server; the product only holds a control-plane connection over WebSocket.
|
|
299
756
|
*
|
|
300
757
|
* Obtain via `createHallModule(options)` from `@soulcraft/sdk/server`.
|
|
301
758
|
*
|
|
@@ -330,11 +787,27 @@ export interface HallModule {
|
|
|
330
787
|
* @throws {Error} If auth fails or the connection cannot be established.
|
|
331
788
|
*/
|
|
332
789
|
connect(): Promise<void>;
|
|
790
|
+
/**
|
|
791
|
+
* Registers a listener for unexpected disconnects (not triggered by `close()`).
|
|
792
|
+
*
|
|
793
|
+
* @param listener - Called with a human-readable disconnect reason.
|
|
794
|
+
*/
|
|
795
|
+
onDisconnect(listener: (reason: string) => void): void;
|
|
796
|
+
/**
|
|
797
|
+
* Registers a listener for successful reconnections.
|
|
798
|
+
* On reconnect, rooms from before the disconnect are gone — re-create them.
|
|
799
|
+
*/
|
|
800
|
+
onReconnect(listener: () => void): void;
|
|
801
|
+
/**
|
|
802
|
+
* Gracefully closes the Hall connection and releases resources.
|
|
803
|
+
* Called automatically by `sdk.shutdown()` in server mode.
|
|
804
|
+
*/
|
|
805
|
+
close(): Promise<void>;
|
|
333
806
|
/**
|
|
334
807
|
* Creates a new room. Errors if a room with the same ID already exists.
|
|
335
808
|
*
|
|
336
809
|
* @param roomId - Unique room identifier (e.g. Brainy session entity ID).
|
|
337
|
-
* @param options - Room options: peer limit, transcription, recording, concept list.
|
|
810
|
+
* @param options - Room options: peer limit, transcription, recording, broadcast, concept list.
|
|
338
811
|
* @returns The HallRoom handle — register event listeners on it immediately.
|
|
339
812
|
*/
|
|
340
813
|
createRoom(roomId: string, options?: RoomOptions): Promise<HallRoom>;
|
|
@@ -357,15 +830,16 @@ export interface HallModule {
|
|
|
357
830
|
*/
|
|
358
831
|
getRoom(roomId: string): HallRoom | undefined;
|
|
359
832
|
/**
|
|
360
|
-
* Mints a short-lived session token for a browser client.
|
|
833
|
+
* Mints a short-lived session token for a browser client to join a room.
|
|
361
834
|
* Pass `{ token, hallUrl }` to the browser — never the product secret.
|
|
362
835
|
*
|
|
363
836
|
* @param roomId - The room the browser will join.
|
|
364
837
|
* @param peerId - The peer's unique identifier (e.g. authenticated user ID).
|
|
365
838
|
* @param ttlSecs - Token lifetime in seconds (default: 300).
|
|
839
|
+
* @param role - Optional role hint: `"participant"`, `"viewer"`, or undefined (auto).
|
|
366
840
|
* @returns `{ token, expiresAt }`.
|
|
367
841
|
*/
|
|
368
|
-
createSessionToken(roomId: string, peerId: string, ttlSecs?: number): Promise<{
|
|
842
|
+
createSessionToken(roomId: string, peerId: string, ttlSecs?: number, role?: HallPeerRole): Promise<{
|
|
369
843
|
token: string;
|
|
370
844
|
expiresAt: number;
|
|
371
845
|
}>;
|
|
@@ -384,21 +858,128 @@ export interface HallModule {
|
|
|
384
858
|
*/
|
|
385
859
|
stopRecording(roomId: string): Promise<void>;
|
|
386
860
|
/**
|
|
387
|
-
*
|
|
861
|
+
* Subscribe to a pub/sub topic. Topics are product-scoped — no cross-product visibility.
|
|
862
|
+
* The returned event fires on the connection-level `onTopicSubscribed` listener.
|
|
388
863
|
*
|
|
389
|
-
* @param
|
|
864
|
+
* @param topic - Topic name to subscribe to.
|
|
865
|
+
* @param metadata - Optional presence metadata visible to other subscribers.
|
|
390
866
|
*/
|
|
391
|
-
|
|
867
|
+
subscribeTopic(topic: string, metadata?: Record<string, unknown>): void;
|
|
392
868
|
/**
|
|
393
|
-
*
|
|
394
|
-
*
|
|
869
|
+
* Unsubscribe from a pub/sub topic.
|
|
870
|
+
*
|
|
871
|
+
* @param topic - Topic name to unsubscribe from.
|
|
395
872
|
*/
|
|
396
|
-
|
|
873
|
+
unsubscribeTopic(topic: string): void;
|
|
397
874
|
/**
|
|
398
|
-
*
|
|
399
|
-
*
|
|
875
|
+
* Broadcast a message to all subscribers of a pub/sub topic.
|
|
876
|
+
*
|
|
877
|
+
* @param topic - Topic to broadcast to.
|
|
878
|
+
* @param payload - Arbitrary JSON payload delivered to all subscribers.
|
|
400
879
|
*/
|
|
401
|
-
|
|
880
|
+
broadcastTopic(topic: string, payload: unknown): void;
|
|
881
|
+
/**
|
|
882
|
+
* Request a snapshot of all current subscribers on a topic.
|
|
883
|
+
* The result fires on the connection-level `onPresenceSnapshot` listener.
|
|
884
|
+
*
|
|
885
|
+
* @param topic - Topic to query presence for.
|
|
886
|
+
*/
|
|
887
|
+
getPresence(topic: string): void;
|
|
888
|
+
/**
|
|
889
|
+
* Register a listener for pub/sub events on the product connection.
|
|
890
|
+
*
|
|
891
|
+
* @param event - The pub/sub event name.
|
|
892
|
+
* @param listener - Callback invoked with the typed event payload.
|
|
893
|
+
*/
|
|
894
|
+
onPubsub<K extends keyof HallPubsubEvents>(event: K, listener: (data: HallPubsubEvents[K]) => void): void;
|
|
895
|
+
/**
|
|
896
|
+
* Issue a browser pub/sub token. The browser uses this to connect to
|
|
897
|
+
* `wss://hall.soulcraft.com/ws/pubsub/{token}` for client-side pub/sub.
|
|
898
|
+
*
|
|
899
|
+
* @param peerId - Peer ID the token is issued for.
|
|
900
|
+
* @param allowedTopics - Optional topic whitelist. If omitted, grants access to all topics.
|
|
901
|
+
* @param ttlSecs - Token lifetime in seconds (default: 300).
|
|
902
|
+
* @returns `{ token, expiresAt }`.
|
|
903
|
+
*/
|
|
904
|
+
createPubsubToken(peerId: string, allowedTopics?: string[], ttlSecs?: number): Promise<{
|
|
905
|
+
token: string;
|
|
906
|
+
expiresAt: number;
|
|
907
|
+
}>;
|
|
908
|
+
/**
|
|
909
|
+
* Promote a viewer to a full bidirectional participant in a broadcast room.
|
|
910
|
+
*
|
|
911
|
+
* @param roomId - Room containing the peer.
|
|
912
|
+
* @param peerId - Peer ID to promote.
|
|
913
|
+
*/
|
|
914
|
+
promotePeer(roomId: string, peerId: string): void;
|
|
915
|
+
/**
|
|
916
|
+
* Demote a participant to a receive-only viewer in a broadcast room.
|
|
917
|
+
*
|
|
918
|
+
* @param roomId - Room containing the peer.
|
|
919
|
+
* @param peerId - Peer ID to demote.
|
|
920
|
+
*/
|
|
921
|
+
demotePeer(roomId: string, peerId: string): void;
|
|
922
|
+
/**
|
|
923
|
+
* Set the screen share simulcast strategy for a peer.
|
|
924
|
+
*
|
|
925
|
+
* @param roomId - Room containing the screensharing peer.
|
|
926
|
+
* @param peerId - Peer sharing their screen.
|
|
927
|
+
* @param mode - `"static"` (prioritize resolution) or `"motion"` (prioritize framerate).
|
|
928
|
+
*/
|
|
929
|
+
setScreenShareMode(roomId: string, peerId: string, mode: ScreenShareMode): void;
|
|
930
|
+
/**
|
|
931
|
+
* Request historical chat messages for a room.
|
|
932
|
+
* Chat is also bridged to pub/sub topic `room:{roomId}:chat` with replay buffer.
|
|
933
|
+
*
|
|
934
|
+
* @param roomId - Room to get chat history for.
|
|
935
|
+
* @param lastN - Maximum number of recent messages to return (default: all stored).
|
|
936
|
+
*/
|
|
937
|
+
getChatHistory(roomId: string, lastN?: number): void;
|
|
938
|
+
/**
|
|
939
|
+
* Revoke a session token. The token becomes invalid immediately — any browser
|
|
940
|
+
* client attempting to connect with it will be rejected.
|
|
941
|
+
*
|
|
942
|
+
* @param token - The session token string to revoke.
|
|
943
|
+
*/
|
|
944
|
+
revokeToken(token: string): void;
|
|
945
|
+
/**
|
|
946
|
+
* Register a listener for token revocation confirmations.
|
|
947
|
+
*
|
|
948
|
+
* @param listener - Called with the token hash (not the token itself) on confirmation.
|
|
949
|
+
*/
|
|
950
|
+
onTokenRevoked(listener: (data: TokenRevokedEvent) => void): void;
|
|
951
|
+
/**
|
|
952
|
+
* Create a WHIP ingest session for an external publisher (OBS, FFmpeg, GStreamer).
|
|
953
|
+
* The publisher's media is injected into the room as a named peer.
|
|
954
|
+
*
|
|
955
|
+
* @param roomId - Room to publish into (must already exist).
|
|
956
|
+
* @param sdpOffer - Raw SDP offer from the publisher.
|
|
957
|
+
* @returns WHIP session with `resourceId` and `answerSdp`.
|
|
958
|
+
*/
|
|
959
|
+
createWhipSession(roomId: string, sdpOffer: string): Promise<WhipSession>;
|
|
960
|
+
/**
|
|
961
|
+
* Send a trickle ICE candidate to an active WHIP session.
|
|
962
|
+
*
|
|
963
|
+
* @param roomId - Room the WHIP session belongs to.
|
|
964
|
+
* @param resourceId - Resource ID from `createWhipSession`.
|
|
965
|
+
* @param candidate - ICE candidate in `application/trickle-ice-sdpfrag` format.
|
|
966
|
+
*/
|
|
967
|
+
addWhipIceCandidate(roomId: string, resourceId: string, candidate: string): Promise<void>;
|
|
968
|
+
/**
|
|
969
|
+
* Tear down a WHIP session and remove the publisher peer from the room.
|
|
970
|
+
*
|
|
971
|
+
* @param roomId - Room the WHIP session belongs to.
|
|
972
|
+
* @param resourceId - Resource ID from `createWhipSession`.
|
|
973
|
+
*/
|
|
974
|
+
closeWhipSession(roomId: string, resourceId: string): Promise<void>;
|
|
975
|
+
}
|
|
976
|
+
/** Event map for pub/sub events on the server-side Hall connection. */
|
|
977
|
+
export interface HallPubsubEvents {
|
|
978
|
+
topicSubscribed: TopicSubscribedEvent;
|
|
979
|
+
topicUnsubscribed: TopicUnsubscribedEvent;
|
|
980
|
+
topicMessage: TopicMessageEvent;
|
|
981
|
+
presenceUpdate: PresenceUpdateEvent;
|
|
982
|
+
presenceSnapshot: PresenceSnapshotEvent;
|
|
402
983
|
}
|
|
403
984
|
/**
|
|
404
985
|
* A handle for a live Hall room, returned by `sdk.hall.createRoom()`.
|
|
@@ -408,10 +989,14 @@ export interface HallModule {
|
|
|
408
989
|
*
|
|
409
990
|
* @example
|
|
410
991
|
* ```typescript
|
|
411
|
-
* const room = await hall.createRoom('cohort-123', {
|
|
992
|
+
* const room = await hall.createRoom('cohort-123', {
|
|
993
|
+
* enableTranscription: true,
|
|
994
|
+
* allowBroadcast: true,
|
|
995
|
+
* })
|
|
412
996
|
* room.on('transcript', (t) => console.log(`${t.peerId}: ${t.text}`))
|
|
413
997
|
* room.on('conceptMention', (c) => brain.relate({ from: c.peerId, to: c.nodeId }))
|
|
414
998
|
* room.on('peerJoined', (p) => updatePresence(p.peerId, 'joined'))
|
|
999
|
+
* room.on('viewerCount', (v) => updateDashboard(v.participants, v.whepViewers + v.hlsViewers))
|
|
415
1000
|
* ```
|
|
416
1001
|
*/
|
|
417
1002
|
export interface HallRoom {
|
|
@@ -462,6 +1047,8 @@ export interface HallRoomHandleEvents {
|
|
|
462
1047
|
peerId: string;
|
|
463
1048
|
track: MediaStreamTrack;
|
|
464
1049
|
};
|
|
1050
|
+
/** Role changed (promoted or demoted in a broadcast room). */
|
|
1051
|
+
roleChanged: RoleChangedEvent;
|
|
465
1052
|
/** The Hall server closed the room. */
|
|
466
1053
|
closed: {
|
|
467
1054
|
roomId: string;
|
|
@@ -478,6 +1065,9 @@ export interface HallRoomHandleEvents {
|
|
|
478
1065
|
* (transcript, conceptMention, relationProposed) alongside media track events
|
|
479
1066
|
* so kit apps have a single event surface for the full session experience.
|
|
480
1067
|
*
|
|
1068
|
+
* In broadcast rooms, the `roleChanged` event fires when the peer is promoted
|
|
1069
|
+
* or demoted. The SDK handles transport upgrade/downgrade transparently.
|
|
1070
|
+
*
|
|
481
1071
|
* @example
|
|
482
1072
|
* ```typescript
|
|
483
1073
|
* const room = await joinHallRoom({ token, hallUrl: 'wss://hall.soulcraft.com' })
|
|
@@ -489,7 +1079,7 @@ export interface HallRoomHandleEvents {
|
|
|
489
1079
|
* room.on('transcript', ({ peerId, text, isFinal }) => {
|
|
490
1080
|
* if (isFinal) appendTranscript(peerId, text)
|
|
491
1081
|
* })
|
|
492
|
-
* room.on('
|
|
1082
|
+
* room.on('roleChanged', ({ role, canPublish }) => updateUI(role, canPublish))
|
|
493
1083
|
* ```
|
|
494
1084
|
*/
|
|
495
1085
|
export interface HallRoomHandle {
|
|
@@ -497,6 +1087,10 @@ export interface HallRoomHandle {
|
|
|
497
1087
|
readonly roomId: string;
|
|
498
1088
|
/** The peer ID for this connection (decoded from the session token). */
|
|
499
1089
|
readonly peerId: string;
|
|
1090
|
+
/** The assigned role in the room. Updated when `roleChanged` fires. */
|
|
1091
|
+
readonly role: HallPeerRole;
|
|
1092
|
+
/** Whether this peer can publish media tracks. Updated when `roleChanged` fires. */
|
|
1093
|
+
readonly canPublish: boolean;
|
|
500
1094
|
/**
|
|
501
1095
|
* Register a listener for a room event.
|
|
502
1096
|
*
|
|
@@ -524,4 +1118,90 @@ export interface HallRoomHandle {
|
|
|
524
1118
|
*/
|
|
525
1119
|
close(): void;
|
|
526
1120
|
}
|
|
1121
|
+
/** Events emitted by a `HallPubsubHandle` in the browser. */
|
|
1122
|
+
export interface HallPubsubHandleEvents {
|
|
1123
|
+
/** Topic subscription confirmed. */
|
|
1124
|
+
topicSubscribed: TopicSubscribedEvent;
|
|
1125
|
+
/** Topic unsubscription confirmed. */
|
|
1126
|
+
topicUnsubscribed: TopicUnsubscribedEvent;
|
|
1127
|
+
/** Message received on a subscribed topic. */
|
|
1128
|
+
topicMessage: TopicMessageEvent;
|
|
1129
|
+
/** A subscriber joined or left a topic. */
|
|
1130
|
+
presenceUpdate: PresenceUpdateEvent;
|
|
1131
|
+
/** Full presence snapshot for a topic. */
|
|
1132
|
+
presenceSnapshot: PresenceSnapshotEvent;
|
|
1133
|
+
/** A connection or protocol error occurred. */
|
|
1134
|
+
error: {
|
|
1135
|
+
code: string;
|
|
1136
|
+
message: string;
|
|
1137
|
+
};
|
|
1138
|
+
/** The pub/sub connection was closed. */
|
|
1139
|
+
closed: void;
|
|
1140
|
+
}
|
|
1141
|
+
/**
|
|
1142
|
+
* An active pub/sub connection to Hall, returned by `joinHallPubsub()`.
|
|
1143
|
+
*
|
|
1144
|
+
* Wraps a WebSocket connection to `wss://hall.soulcraft.com/ws/pubsub/{token}`.
|
|
1145
|
+
* Uses msgpack wire format, same as the product WebSocket.
|
|
1146
|
+
*
|
|
1147
|
+
* @example
|
|
1148
|
+
* ```typescript
|
|
1149
|
+
* import { joinHallPubsub } from '@soulcraft/sdk/client'
|
|
1150
|
+
*
|
|
1151
|
+
* const pubsub = await joinHallPubsub({ token, hallUrl: 'wss://hall.soulcraft.com' })
|
|
1152
|
+
* pubsub.subscribe('room:abc:chat', { username: 'Alice' })
|
|
1153
|
+
* pubsub.on('topicMessage', ({ topic, senderId, payload }) => showMessage(senderId, payload))
|
|
1154
|
+
* pubsub.on('presenceUpdate', ({ peerId, action }) => updatePresence(peerId, action))
|
|
1155
|
+
*
|
|
1156
|
+
* pubsub.broadcast('room:abc:chat', { text: 'Hello!' })
|
|
1157
|
+
* // On leave:
|
|
1158
|
+
* pubsub.close()
|
|
1159
|
+
* ```
|
|
1160
|
+
*/
|
|
1161
|
+
export interface HallPubsubHandle {
|
|
1162
|
+
/** The peer ID for this connection (decoded from the pubsub token). */
|
|
1163
|
+
readonly peerId: string;
|
|
1164
|
+
/**
|
|
1165
|
+
* Subscribe to a pub/sub topic.
|
|
1166
|
+
*
|
|
1167
|
+
* @param topic - Topic name to subscribe to.
|
|
1168
|
+
* @param metadata - Optional presence metadata visible to other subscribers.
|
|
1169
|
+
*/
|
|
1170
|
+
subscribe(topic: string, metadata?: Record<string, unknown>): void;
|
|
1171
|
+
/**
|
|
1172
|
+
* Unsubscribe from a pub/sub topic.
|
|
1173
|
+
*
|
|
1174
|
+
* @param topic - Topic name to unsubscribe from.
|
|
1175
|
+
*/
|
|
1176
|
+
unsubscribe(topic: string): void;
|
|
1177
|
+
/**
|
|
1178
|
+
* Broadcast a message to all subscribers of a topic.
|
|
1179
|
+
*
|
|
1180
|
+
* @param topic - Topic to broadcast to.
|
|
1181
|
+
* @param payload - Arbitrary JSON payload delivered to all subscribers.
|
|
1182
|
+
*/
|
|
1183
|
+
broadcast(topic: string, payload: unknown): void;
|
|
1184
|
+
/**
|
|
1185
|
+
* Request a presence snapshot for a topic.
|
|
1186
|
+
*
|
|
1187
|
+
* @param topic - Topic to query presence for.
|
|
1188
|
+
*/
|
|
1189
|
+
getPresence(topic: string): void;
|
|
1190
|
+
/**
|
|
1191
|
+
* Register a listener for a pub/sub event.
|
|
1192
|
+
*
|
|
1193
|
+
* @param event - The event name.
|
|
1194
|
+
* @param listener - Callback invoked with the typed event payload.
|
|
1195
|
+
*/
|
|
1196
|
+
on<K extends keyof HallPubsubHandleEvents>(event: K, listener: (data: HallPubsubHandleEvents[K]) => void): this;
|
|
1197
|
+
/**
|
|
1198
|
+
* Remove a previously registered listener.
|
|
1199
|
+
*
|
|
1200
|
+
* @param event - The event name.
|
|
1201
|
+
* @param listener - The exact function reference passed to `on`.
|
|
1202
|
+
*/
|
|
1203
|
+
off<K extends keyof HallPubsubHandleEvents>(event: K, listener: (data: HallPubsubHandleEvents[K]) => void): this;
|
|
1204
|
+
/** Disconnect the pub/sub WebSocket. */
|
|
1205
|
+
close(): void;
|
|
1206
|
+
}
|
|
527
1207
|
//# sourceMappingURL=types.d.ts.map
|