@soulcraft/sdk 2.0.0 → 2.0.2
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/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 +83 -6
- package/dist/modules/hall/server.d.ts.map +1 -1
- package/dist/modules/hall/server.js +206 -9
- package/dist/modules/hall/server.js.map +1 -1
- package/dist/modules/hall/types.d.ts +548 -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 +40 -12
- package/dist/server/hall-handlers.d.ts.map +1 -1
- package/dist/server/hall-handlers.js +40 -12
- package/dist/server/hall-handlers.js.map +1 -1
- package/dist/server/handlers/chat/engine.d.ts.map +1 -1
- package/dist/server/handlers/chat/engine.js +5 -1
- package/dist/server/handlers/chat/engine.js.map +1 -1
- package/dist/server/handlers/chat/types.d.ts +17 -2
- package/dist/server/handlers/chat/types.d.ts.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,30 @@ 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;
|
|
75
125
|
}
|
|
76
126
|
/**
|
|
77
127
|
* Manifest emitted when recording stops.
|
|
@@ -89,6 +139,16 @@ export interface RecordingManifest {
|
|
|
89
139
|
audioTracks: string[];
|
|
90
140
|
/** Absolute paths to per-participant video track MKV files. */
|
|
91
141
|
videoTracks: string[];
|
|
142
|
+
/**
|
|
143
|
+
* Path to the composite MP4 (mixed audio + active speaker video).
|
|
144
|
+
* Only present when `RoomOptions.recordingComposite` was true.
|
|
145
|
+
*/
|
|
146
|
+
compositePath?: string;
|
|
147
|
+
/**
|
|
148
|
+
* Cloud storage URLs (if cloud upload was configured in `hall.toml`).
|
|
149
|
+
* Populated after upload completes (GCS, S3, etc.).
|
|
150
|
+
*/
|
|
151
|
+
cloudUrls?: string[];
|
|
92
152
|
}
|
|
93
153
|
/** A Whisper ASR transcript segment from a peer in the room. */
|
|
94
154
|
export interface TranscriptEvent {
|
|
@@ -146,6 +206,179 @@ export interface PeerLeftEvent {
|
|
|
146
206
|
roomId: string;
|
|
147
207
|
peerId: string;
|
|
148
208
|
}
|
|
209
|
+
/** A single replayed message from a topic's replay buffer. */
|
|
210
|
+
export interface ReplayEntry {
|
|
211
|
+
/** Sender identifier (product name or peer ID). */
|
|
212
|
+
senderId: string;
|
|
213
|
+
/** The message payload. */
|
|
214
|
+
payload: unknown;
|
|
215
|
+
/** Unix timestamp in milliseconds when the message was originally sent. */
|
|
216
|
+
timestampMs: number;
|
|
217
|
+
}
|
|
218
|
+
/** Confirmation of a pub/sub topic subscription. */
|
|
219
|
+
export interface TopicSubscribedEvent {
|
|
220
|
+
/** Topic that was subscribed to. */
|
|
221
|
+
topic: string;
|
|
222
|
+
/** Current number of subscribers on this topic. */
|
|
223
|
+
subscriberCount: number;
|
|
224
|
+
/** Replay buffer entries (most recent messages, if any). */
|
|
225
|
+
replay: ReplayEntry[];
|
|
226
|
+
}
|
|
227
|
+
/** Confirmation of a pub/sub topic unsubscription. */
|
|
228
|
+
export interface TopicUnsubscribedEvent {
|
|
229
|
+
/** Topic that was unsubscribed from. */
|
|
230
|
+
topic: string;
|
|
231
|
+
}
|
|
232
|
+
/** A message received on a subscribed pub/sub topic. */
|
|
233
|
+
export interface TopicMessageEvent {
|
|
234
|
+
/** Topic the message was published to. */
|
|
235
|
+
topic: string;
|
|
236
|
+
/** Sender identifier (product name or peer ID). */
|
|
237
|
+
senderId: string;
|
|
238
|
+
/** The message payload. */
|
|
239
|
+
payload: unknown;
|
|
240
|
+
/** Unix timestamp in milliseconds. */
|
|
241
|
+
timestampMs: number;
|
|
242
|
+
}
|
|
243
|
+
/** A subscriber joined or left a pub/sub topic. */
|
|
244
|
+
export interface PresenceUpdateEvent {
|
|
245
|
+
/** Topic the presence event occurred on. */
|
|
246
|
+
topic: string;
|
|
247
|
+
/** Peer who joined or left. */
|
|
248
|
+
peerId: string;
|
|
249
|
+
/** Whether the peer joined or left. */
|
|
250
|
+
action: 'joined' | 'left';
|
|
251
|
+
/** Presence metadata (only present on join). */
|
|
252
|
+
metadata?: Record<string, unknown>;
|
|
253
|
+
/** Current subscriber count after this event. */
|
|
254
|
+
subscriberCount: number;
|
|
255
|
+
}
|
|
256
|
+
/** A single subscriber in a presence snapshot. */
|
|
257
|
+
export interface PresenceEntry {
|
|
258
|
+
/** Subscriber identifier. */
|
|
259
|
+
peerId: string;
|
|
260
|
+
/** Optional presence metadata set at subscribe time. */
|
|
261
|
+
metadata?: Record<string, unknown>;
|
|
262
|
+
}
|
|
263
|
+
/** Full snapshot of all current subscribers on a topic. */
|
|
264
|
+
export interface PresenceSnapshotEvent {
|
|
265
|
+
/** Topic queried. */
|
|
266
|
+
topic: string;
|
|
267
|
+
/** All current subscribers with their metadata. */
|
|
268
|
+
subscribers: PresenceEntry[];
|
|
269
|
+
}
|
|
270
|
+
/** Metadata for a processed media file. */
|
|
271
|
+
export interface MediaInfo {
|
|
272
|
+
/** Unique media identifier. */
|
|
273
|
+
mediaId: string;
|
|
274
|
+
/** Product that uploaded this media. */
|
|
275
|
+
productName: string;
|
|
276
|
+
/** Original filename from the upload. */
|
|
277
|
+
originalFilename: string;
|
|
278
|
+
/** MIME type of the original file. */
|
|
279
|
+
mimeType: string;
|
|
280
|
+
/** File size in bytes. */
|
|
281
|
+
size: number;
|
|
282
|
+
/** Duration in seconds (audio/video only). */
|
|
283
|
+
duration?: number;
|
|
284
|
+
/** Width × height in pixels (image/video only). */
|
|
285
|
+
dimensions?: [number, number];
|
|
286
|
+
/** Whether the file was transcoded from its original format. */
|
|
287
|
+
transcoded: boolean;
|
|
288
|
+
/** Processing status. */
|
|
289
|
+
status: 'processing' | 'ready' | 'error';
|
|
290
|
+
/** ISO 8601 timestamp when the media was uploaded. */
|
|
291
|
+
createdAt: string;
|
|
292
|
+
}
|
|
293
|
+
/** Notification that a media file has been processed and is ready. */
|
|
294
|
+
export interface MediaReadyEvent {
|
|
295
|
+
/** Unique media identifier. */
|
|
296
|
+
mediaId: string;
|
|
297
|
+
/** MIME type of the original file. */
|
|
298
|
+
mimeType: string;
|
|
299
|
+
/** File size in bytes. */
|
|
300
|
+
size: number;
|
|
301
|
+
/** Duration in seconds (audio/video only). */
|
|
302
|
+
duration?: number;
|
|
303
|
+
/** Width × height in pixels (image/video only). */
|
|
304
|
+
dimensions?: [number, number];
|
|
305
|
+
}
|
|
306
|
+
/** Notification that media processing failed. */
|
|
307
|
+
export interface MediaErrorEvent {
|
|
308
|
+
/** Media identifier that failed. */
|
|
309
|
+
mediaId: string;
|
|
310
|
+
/** Human-readable error description. */
|
|
311
|
+
error: string;
|
|
312
|
+
}
|
|
313
|
+
/**
|
|
314
|
+
* Supported transcode target formats for media uploads.
|
|
315
|
+
* Sent as the `transcode` field in the upload form.
|
|
316
|
+
*/
|
|
317
|
+
export type TranscodeTarget = 'audio/mp3' | 'video/mp4' | 'image/webp';
|
|
318
|
+
/**
|
|
319
|
+
* Role assigned to a peer in a broadcast room.
|
|
320
|
+
* - `participant` — full bidirectional WebRTC (can publish and receive media)
|
|
321
|
+
* - `viewer` — receive-only (WHEP or LL-HLS, cannot publish)
|
|
322
|
+
*/
|
|
323
|
+
export type HallPeerRole = 'participant' | 'viewer';
|
|
324
|
+
/** A viewer was promoted to full participant. */
|
|
325
|
+
export interface PeerPromotedEvent {
|
|
326
|
+
roomId: string;
|
|
327
|
+
peerId: string;
|
|
328
|
+
}
|
|
329
|
+
/** A participant was demoted to viewer. */
|
|
330
|
+
export interface PeerDemotedEvent {
|
|
331
|
+
roomId: string;
|
|
332
|
+
peerId: string;
|
|
333
|
+
}
|
|
334
|
+
/** Periodic viewer count update for broadcast rooms (every ~5s). */
|
|
335
|
+
export interface ViewerCountEvent {
|
|
336
|
+
roomId: string;
|
|
337
|
+
/** Number of full bidirectional participants. */
|
|
338
|
+
participants: number;
|
|
339
|
+
/** Number of WHEP (sub-second latency) viewers. */
|
|
340
|
+
whepViewers: number;
|
|
341
|
+
/** Number of LL-HLS viewers. */
|
|
342
|
+
hlsViewers: number;
|
|
343
|
+
}
|
|
344
|
+
/**
|
|
345
|
+
* Screen share simulcast mode.
|
|
346
|
+
* - `static` — prioritize resolution (presentations, documents)
|
|
347
|
+
* - `motion` — prioritize framerate (demos, video playback)
|
|
348
|
+
*/
|
|
349
|
+
export type ScreenShareMode = 'static' | 'motion';
|
|
350
|
+
/** Periodic thumbnail of an active screen share. */
|
|
351
|
+
export interface ScreenShareThumbnailEvent {
|
|
352
|
+
roomId: string;
|
|
353
|
+
peerId: string;
|
|
354
|
+
/** URL to the latest thumbnail image. */
|
|
355
|
+
thumbnailUrl: string;
|
|
356
|
+
}
|
|
357
|
+
/** A single chat message in room history. */
|
|
358
|
+
export interface ChatMessage {
|
|
359
|
+
/** Peer who sent the message. */
|
|
360
|
+
peerId: string;
|
|
361
|
+
/** Message text. */
|
|
362
|
+
text: string;
|
|
363
|
+
/** Unix timestamp in milliseconds. */
|
|
364
|
+
timestampMs: number;
|
|
365
|
+
}
|
|
366
|
+
/** Response to a `getChatHistory` request. */
|
|
367
|
+
export interface ChatHistoryEvent {
|
|
368
|
+
roomId: string;
|
|
369
|
+
/** Chat messages in chronological order. */
|
|
370
|
+
messages: ChatMessage[];
|
|
371
|
+
}
|
|
372
|
+
/**
|
|
373
|
+
* Sent to a browser client when their role changes (promoted or demoted).
|
|
374
|
+
* The SDK handles transport upgrade/downgrade transparently.
|
|
375
|
+
*/
|
|
376
|
+
export interface RoleChangedEvent {
|
|
377
|
+
/** New role: `"participant"` or `"viewer"`. */
|
|
378
|
+
role: HallPeerRole;
|
|
379
|
+
/** Whether the peer can now publish media tracks. */
|
|
380
|
+
canPublish: boolean;
|
|
381
|
+
}
|
|
149
382
|
/**
|
|
150
383
|
* Messages sent from the product backend to the Hall server over the product WebSocket.
|
|
151
384
|
* Encoded as msgpack with `t` (type tag) and `d` (data) fields — matching Rust serde
|
|
@@ -179,6 +412,7 @@ export type HallClientMessage = {
|
|
|
179
412
|
roomId: string;
|
|
180
413
|
peerId: string;
|
|
181
414
|
ttlSecs?: number | undefined;
|
|
415
|
+
role?: HallPeerRole | undefined;
|
|
182
416
|
};
|
|
183
417
|
} | {
|
|
184
418
|
t: 'startRecording';
|
|
@@ -197,6 +431,60 @@ export type HallClientMessage = {
|
|
|
197
431
|
channel: string;
|
|
198
432
|
payload: Uint8Array;
|
|
199
433
|
};
|
|
434
|
+
} | {
|
|
435
|
+
t: 'subscribeTopic';
|
|
436
|
+
d: {
|
|
437
|
+
topic: string;
|
|
438
|
+
metadata?: Record<string, unknown> | undefined;
|
|
439
|
+
};
|
|
440
|
+
} | {
|
|
441
|
+
t: 'unsubscribeTopic';
|
|
442
|
+
d: {
|
|
443
|
+
topic: string;
|
|
444
|
+
};
|
|
445
|
+
} | {
|
|
446
|
+
t: 'broadcastTopic';
|
|
447
|
+
d: {
|
|
448
|
+
topic: string;
|
|
449
|
+
payload: unknown;
|
|
450
|
+
};
|
|
451
|
+
} | {
|
|
452
|
+
t: 'getPresence';
|
|
453
|
+
d: {
|
|
454
|
+
topic: string;
|
|
455
|
+
};
|
|
456
|
+
} | {
|
|
457
|
+
t: 'createPubsubToken';
|
|
458
|
+
d: {
|
|
459
|
+
peerId: string;
|
|
460
|
+
allowedTopics?: string[] | undefined;
|
|
461
|
+
ttlSecs?: number | undefined;
|
|
462
|
+
};
|
|
463
|
+
} | {
|
|
464
|
+
t: 'promotePeer';
|
|
465
|
+
d: {
|
|
466
|
+
roomId: string;
|
|
467
|
+
peerId: string;
|
|
468
|
+
};
|
|
469
|
+
} | {
|
|
470
|
+
t: 'demotePeer';
|
|
471
|
+
d: {
|
|
472
|
+
roomId: string;
|
|
473
|
+
peerId: string;
|
|
474
|
+
};
|
|
475
|
+
} | {
|
|
476
|
+
t: 'setScreenShareMode';
|
|
477
|
+
d: {
|
|
478
|
+
roomId: string;
|
|
479
|
+
peerId: string;
|
|
480
|
+
mode: ScreenShareMode;
|
|
481
|
+
};
|
|
482
|
+
} | {
|
|
483
|
+
t: 'getChatHistory';
|
|
484
|
+
d: {
|
|
485
|
+
roomId: string;
|
|
486
|
+
lastN?: number | undefined;
|
|
487
|
+
};
|
|
200
488
|
};
|
|
201
489
|
/**
|
|
202
490
|
* Messages sent from the Hall server to the product backend.
|
|
@@ -276,6 +564,48 @@ export type HallServerMessage = {
|
|
|
276
564
|
} | {
|
|
277
565
|
t: 'peerLeft';
|
|
278
566
|
d: PeerLeftEvent;
|
|
567
|
+
} | {
|
|
568
|
+
t: 'topicSubscribed';
|
|
569
|
+
d: TopicSubscribedEvent;
|
|
570
|
+
} | {
|
|
571
|
+
t: 'topicUnsubscribed';
|
|
572
|
+
d: TopicUnsubscribedEvent;
|
|
573
|
+
} | {
|
|
574
|
+
t: 'topicMessage';
|
|
575
|
+
d: TopicMessageEvent;
|
|
576
|
+
} | {
|
|
577
|
+
t: 'presenceUpdate';
|
|
578
|
+
d: PresenceUpdateEvent;
|
|
579
|
+
} | {
|
|
580
|
+
t: 'presenceSnapshot';
|
|
581
|
+
d: PresenceSnapshotEvent;
|
|
582
|
+
} | {
|
|
583
|
+
t: 'pubsubToken';
|
|
584
|
+
d: {
|
|
585
|
+
token: string;
|
|
586
|
+
expiresAt: number;
|
|
587
|
+
};
|
|
588
|
+
} | {
|
|
589
|
+
t: 'mediaReady';
|
|
590
|
+
d: MediaReadyEvent;
|
|
591
|
+
} | {
|
|
592
|
+
t: 'mediaError';
|
|
593
|
+
d: MediaErrorEvent;
|
|
594
|
+
} | {
|
|
595
|
+
t: 'peerPromoted';
|
|
596
|
+
d: PeerPromotedEvent;
|
|
597
|
+
} | {
|
|
598
|
+
t: 'peerDemoted';
|
|
599
|
+
d: PeerDemotedEvent;
|
|
600
|
+
} | {
|
|
601
|
+
t: 'viewerCount';
|
|
602
|
+
d: ViewerCountEvent;
|
|
603
|
+
} | {
|
|
604
|
+
t: 'screenShareThumbnail';
|
|
605
|
+
d: ScreenShareThumbnailEvent;
|
|
606
|
+
} | {
|
|
607
|
+
t: 'chatHistory';
|
|
608
|
+
d: ChatHistoryEvent;
|
|
279
609
|
};
|
|
280
610
|
/** Event map for server-side {@link HallRoom} listeners. */
|
|
281
611
|
export interface HallRoomEvents {
|
|
@@ -286,6 +616,13 @@ export interface HallRoomEvents {
|
|
|
286
616
|
peerJoined: PeerJoinedEvent;
|
|
287
617
|
peerLeft: PeerLeftEvent;
|
|
288
618
|
recordingManifest: RecordingManifest;
|
|
619
|
+
peerPromoted: PeerPromotedEvent;
|
|
620
|
+
peerDemoted: PeerDemotedEvent;
|
|
621
|
+
viewerCount: ViewerCountEvent;
|
|
622
|
+
screenShareThumbnail: ScreenShareThumbnailEvent;
|
|
623
|
+
chatHistory: ChatHistoryEvent;
|
|
624
|
+
mediaReady: MediaReadyEvent;
|
|
625
|
+
mediaError: MediaErrorEvent;
|
|
289
626
|
closed: {
|
|
290
627
|
roomId: string;
|
|
291
628
|
};
|
|
@@ -293,9 +630,9 @@ export interface HallRoomEvents {
|
|
|
293
630
|
/**
|
|
294
631
|
* The `sdk.hall` namespace on `SoulcraftSDK` in **server mode**.
|
|
295
632
|
*
|
|
296
|
-
* Products call these methods in request handlers to create rooms
|
|
297
|
-
*
|
|
298
|
-
* the product only holds a control-plane connection over WebSocket.
|
|
633
|
+
* Products call these methods in request handlers to create rooms, manage pub/sub
|
|
634
|
+
* topics, upload media, and issue session tokens. Rooms and topics are managed by
|
|
635
|
+
* the Hall server; the product only holds a control-plane connection over WebSocket.
|
|
299
636
|
*
|
|
300
637
|
* Obtain via `createHallModule(options)` from `@soulcraft/sdk/server`.
|
|
301
638
|
*
|
|
@@ -330,11 +667,27 @@ export interface HallModule {
|
|
|
330
667
|
* @throws {Error} If auth fails or the connection cannot be established.
|
|
331
668
|
*/
|
|
332
669
|
connect(): Promise<void>;
|
|
670
|
+
/**
|
|
671
|
+
* Registers a listener for unexpected disconnects (not triggered by `close()`).
|
|
672
|
+
*
|
|
673
|
+
* @param listener - Called with a human-readable disconnect reason.
|
|
674
|
+
*/
|
|
675
|
+
onDisconnect(listener: (reason: string) => void): void;
|
|
676
|
+
/**
|
|
677
|
+
* Registers a listener for successful reconnections.
|
|
678
|
+
* On reconnect, rooms from before the disconnect are gone — re-create them.
|
|
679
|
+
*/
|
|
680
|
+
onReconnect(listener: () => void): void;
|
|
681
|
+
/**
|
|
682
|
+
* Gracefully closes the Hall connection and releases resources.
|
|
683
|
+
* Called automatically by `sdk.shutdown()` in server mode.
|
|
684
|
+
*/
|
|
685
|
+
close(): Promise<void>;
|
|
333
686
|
/**
|
|
334
687
|
* Creates a new room. Errors if a room with the same ID already exists.
|
|
335
688
|
*
|
|
336
689
|
* @param roomId - Unique room identifier (e.g. Brainy session entity ID).
|
|
337
|
-
* @param options - Room options: peer limit, transcription, recording, concept list.
|
|
690
|
+
* @param options - Room options: peer limit, transcription, recording, broadcast, concept list.
|
|
338
691
|
* @returns The HallRoom handle — register event listeners on it immediately.
|
|
339
692
|
*/
|
|
340
693
|
createRoom(roomId: string, options?: RoomOptions): Promise<HallRoom>;
|
|
@@ -357,15 +710,16 @@ export interface HallModule {
|
|
|
357
710
|
*/
|
|
358
711
|
getRoom(roomId: string): HallRoom | undefined;
|
|
359
712
|
/**
|
|
360
|
-
* Mints a short-lived session token for a browser client.
|
|
713
|
+
* Mints a short-lived session token for a browser client to join a room.
|
|
361
714
|
* Pass `{ token, hallUrl }` to the browser — never the product secret.
|
|
362
715
|
*
|
|
363
716
|
* @param roomId - The room the browser will join.
|
|
364
717
|
* @param peerId - The peer's unique identifier (e.g. authenticated user ID).
|
|
365
718
|
* @param ttlSecs - Token lifetime in seconds (default: 300).
|
|
719
|
+
* @param role - Optional role hint: `"participant"`, `"viewer"`, or undefined (auto).
|
|
366
720
|
* @returns `{ token, expiresAt }`.
|
|
367
721
|
*/
|
|
368
|
-
createSessionToken(roomId: string, peerId: string, ttlSecs?: number): Promise<{
|
|
722
|
+
createSessionToken(roomId: string, peerId: string, ttlSecs?: number, role?: HallPeerRole): Promise<{
|
|
369
723
|
token: string;
|
|
370
724
|
expiresAt: number;
|
|
371
725
|
}>;
|
|
@@ -384,21 +738,91 @@ export interface HallModule {
|
|
|
384
738
|
*/
|
|
385
739
|
stopRecording(roomId: string): Promise<void>;
|
|
386
740
|
/**
|
|
387
|
-
*
|
|
741
|
+
* Subscribe to a pub/sub topic. Topics are product-scoped — no cross-product visibility.
|
|
742
|
+
* The returned event fires on the connection-level `onTopicSubscribed` listener.
|
|
388
743
|
*
|
|
389
|
-
* @param
|
|
744
|
+
* @param topic - Topic name to subscribe to.
|
|
745
|
+
* @param metadata - Optional presence metadata visible to other subscribers.
|
|
390
746
|
*/
|
|
391
|
-
|
|
747
|
+
subscribeTopic(topic: string, metadata?: Record<string, unknown>): void;
|
|
392
748
|
/**
|
|
393
|
-
*
|
|
394
|
-
*
|
|
749
|
+
* Unsubscribe from a pub/sub topic.
|
|
750
|
+
*
|
|
751
|
+
* @param topic - Topic name to unsubscribe from.
|
|
395
752
|
*/
|
|
396
|
-
|
|
753
|
+
unsubscribeTopic(topic: string): void;
|
|
397
754
|
/**
|
|
398
|
-
*
|
|
399
|
-
*
|
|
755
|
+
* Broadcast a message to all subscribers of a pub/sub topic.
|
|
756
|
+
*
|
|
757
|
+
* @param topic - Topic to broadcast to.
|
|
758
|
+
* @param payload - Arbitrary JSON payload delivered to all subscribers.
|
|
400
759
|
*/
|
|
401
|
-
|
|
760
|
+
broadcastTopic(topic: string, payload: unknown): void;
|
|
761
|
+
/**
|
|
762
|
+
* Request a snapshot of all current subscribers on a topic.
|
|
763
|
+
* The result fires on the connection-level `onPresenceSnapshot` listener.
|
|
764
|
+
*
|
|
765
|
+
* @param topic - Topic to query presence for.
|
|
766
|
+
*/
|
|
767
|
+
getPresence(topic: string): void;
|
|
768
|
+
/**
|
|
769
|
+
* Register a listener for pub/sub events on the product connection.
|
|
770
|
+
*
|
|
771
|
+
* @param event - The pub/sub event name.
|
|
772
|
+
* @param listener - Callback invoked with the typed event payload.
|
|
773
|
+
*/
|
|
774
|
+
onPubsub<K extends keyof HallPubsubEvents>(event: K, listener: (data: HallPubsubEvents[K]) => void): void;
|
|
775
|
+
/**
|
|
776
|
+
* Issue a browser pub/sub token. The browser uses this to connect to
|
|
777
|
+
* `wss://hall.soulcraft.com/ws/pubsub/{token}` for client-side pub/sub.
|
|
778
|
+
*
|
|
779
|
+
* @param peerId - Peer ID the token is issued for.
|
|
780
|
+
* @param allowedTopics - Optional topic whitelist. If omitted, grants access to all topics.
|
|
781
|
+
* @param ttlSecs - Token lifetime in seconds (default: 300).
|
|
782
|
+
* @returns `{ token, expiresAt }`.
|
|
783
|
+
*/
|
|
784
|
+
createPubsubToken(peerId: string, allowedTopics?: string[], ttlSecs?: number): Promise<{
|
|
785
|
+
token: string;
|
|
786
|
+
expiresAt: number;
|
|
787
|
+
}>;
|
|
788
|
+
/**
|
|
789
|
+
* Promote a viewer to a full bidirectional participant in a broadcast room.
|
|
790
|
+
*
|
|
791
|
+
* @param roomId - Room containing the peer.
|
|
792
|
+
* @param peerId - Peer ID to promote.
|
|
793
|
+
*/
|
|
794
|
+
promotePeer(roomId: string, peerId: string): void;
|
|
795
|
+
/**
|
|
796
|
+
* Demote a participant to a receive-only viewer in a broadcast room.
|
|
797
|
+
*
|
|
798
|
+
* @param roomId - Room containing the peer.
|
|
799
|
+
* @param peerId - Peer ID to demote.
|
|
800
|
+
*/
|
|
801
|
+
demotePeer(roomId: string, peerId: string): void;
|
|
802
|
+
/**
|
|
803
|
+
* Set the screen share simulcast strategy for a peer.
|
|
804
|
+
*
|
|
805
|
+
* @param roomId - Room containing the screensharing peer.
|
|
806
|
+
* @param peerId - Peer sharing their screen.
|
|
807
|
+
* @param mode - `"static"` (prioritize resolution) or `"motion"` (prioritize framerate).
|
|
808
|
+
*/
|
|
809
|
+
setScreenShareMode(roomId: string, peerId: string, mode: ScreenShareMode): void;
|
|
810
|
+
/**
|
|
811
|
+
* Request historical chat messages for a room.
|
|
812
|
+
* Chat is also bridged to pub/sub topic `room:{roomId}:chat` with replay buffer.
|
|
813
|
+
*
|
|
814
|
+
* @param roomId - Room to get chat history for.
|
|
815
|
+
* @param lastN - Maximum number of recent messages to return (default: all stored).
|
|
816
|
+
*/
|
|
817
|
+
getChatHistory(roomId: string, lastN?: number): void;
|
|
818
|
+
}
|
|
819
|
+
/** Event map for pub/sub events on the server-side Hall connection. */
|
|
820
|
+
export interface HallPubsubEvents {
|
|
821
|
+
topicSubscribed: TopicSubscribedEvent;
|
|
822
|
+
topicUnsubscribed: TopicUnsubscribedEvent;
|
|
823
|
+
topicMessage: TopicMessageEvent;
|
|
824
|
+
presenceUpdate: PresenceUpdateEvent;
|
|
825
|
+
presenceSnapshot: PresenceSnapshotEvent;
|
|
402
826
|
}
|
|
403
827
|
/**
|
|
404
828
|
* A handle for a live Hall room, returned by `sdk.hall.createRoom()`.
|
|
@@ -408,10 +832,14 @@ export interface HallModule {
|
|
|
408
832
|
*
|
|
409
833
|
* @example
|
|
410
834
|
* ```typescript
|
|
411
|
-
* const room = await hall.createRoom('cohort-123', {
|
|
835
|
+
* const room = await hall.createRoom('cohort-123', {
|
|
836
|
+
* enableTranscription: true,
|
|
837
|
+
* allowBroadcast: true,
|
|
838
|
+
* })
|
|
412
839
|
* room.on('transcript', (t) => console.log(`${t.peerId}: ${t.text}`))
|
|
413
840
|
* room.on('conceptMention', (c) => brain.relate({ from: c.peerId, to: c.nodeId }))
|
|
414
841
|
* room.on('peerJoined', (p) => updatePresence(p.peerId, 'joined'))
|
|
842
|
+
* room.on('viewerCount', (v) => updateDashboard(v.participants, v.whepViewers + v.hlsViewers))
|
|
415
843
|
* ```
|
|
416
844
|
*/
|
|
417
845
|
export interface HallRoom {
|
|
@@ -462,6 +890,8 @@ export interface HallRoomHandleEvents {
|
|
|
462
890
|
peerId: string;
|
|
463
891
|
track: MediaStreamTrack;
|
|
464
892
|
};
|
|
893
|
+
/** Role changed (promoted or demoted in a broadcast room). */
|
|
894
|
+
roleChanged: RoleChangedEvent;
|
|
465
895
|
/** The Hall server closed the room. */
|
|
466
896
|
closed: {
|
|
467
897
|
roomId: string;
|
|
@@ -478,6 +908,9 @@ export interface HallRoomHandleEvents {
|
|
|
478
908
|
* (transcript, conceptMention, relationProposed) alongside media track events
|
|
479
909
|
* so kit apps have a single event surface for the full session experience.
|
|
480
910
|
*
|
|
911
|
+
* In broadcast rooms, the `roleChanged` event fires when the peer is promoted
|
|
912
|
+
* or demoted. The SDK handles transport upgrade/downgrade transparently.
|
|
913
|
+
*
|
|
481
914
|
* @example
|
|
482
915
|
* ```typescript
|
|
483
916
|
* const room = await joinHallRoom({ token, hallUrl: 'wss://hall.soulcraft.com' })
|
|
@@ -489,7 +922,7 @@ export interface HallRoomHandleEvents {
|
|
|
489
922
|
* room.on('transcript', ({ peerId, text, isFinal }) => {
|
|
490
923
|
* if (isFinal) appendTranscript(peerId, text)
|
|
491
924
|
* })
|
|
492
|
-
* room.on('
|
|
925
|
+
* room.on('roleChanged', ({ role, canPublish }) => updateUI(role, canPublish))
|
|
493
926
|
* ```
|
|
494
927
|
*/
|
|
495
928
|
export interface HallRoomHandle {
|
|
@@ -497,6 +930,10 @@ export interface HallRoomHandle {
|
|
|
497
930
|
readonly roomId: string;
|
|
498
931
|
/** The peer ID for this connection (decoded from the session token). */
|
|
499
932
|
readonly peerId: string;
|
|
933
|
+
/** The assigned role in the room. Updated when `roleChanged` fires. */
|
|
934
|
+
readonly role: HallPeerRole;
|
|
935
|
+
/** Whether this peer can publish media tracks. Updated when `roleChanged` fires. */
|
|
936
|
+
readonly canPublish: boolean;
|
|
500
937
|
/**
|
|
501
938
|
* Register a listener for a room event.
|
|
502
939
|
*
|
|
@@ -524,4 +961,90 @@ export interface HallRoomHandle {
|
|
|
524
961
|
*/
|
|
525
962
|
close(): void;
|
|
526
963
|
}
|
|
964
|
+
/** Events emitted by a `HallPubsubHandle` in the browser. */
|
|
965
|
+
export interface HallPubsubHandleEvents {
|
|
966
|
+
/** Topic subscription confirmed. */
|
|
967
|
+
topicSubscribed: TopicSubscribedEvent;
|
|
968
|
+
/** Topic unsubscription confirmed. */
|
|
969
|
+
topicUnsubscribed: TopicUnsubscribedEvent;
|
|
970
|
+
/** Message received on a subscribed topic. */
|
|
971
|
+
topicMessage: TopicMessageEvent;
|
|
972
|
+
/** A subscriber joined or left a topic. */
|
|
973
|
+
presenceUpdate: PresenceUpdateEvent;
|
|
974
|
+
/** Full presence snapshot for a topic. */
|
|
975
|
+
presenceSnapshot: PresenceSnapshotEvent;
|
|
976
|
+
/** A connection or protocol error occurred. */
|
|
977
|
+
error: {
|
|
978
|
+
code: string;
|
|
979
|
+
message: string;
|
|
980
|
+
};
|
|
981
|
+
/** The pub/sub connection was closed. */
|
|
982
|
+
closed: void;
|
|
983
|
+
}
|
|
984
|
+
/**
|
|
985
|
+
* An active pub/sub connection to Hall, returned by `joinHallPubsub()`.
|
|
986
|
+
*
|
|
987
|
+
* Wraps a WebSocket connection to `wss://hall.soulcraft.com/ws/pubsub/{token}`.
|
|
988
|
+
* Uses msgpack wire format, same as the product WebSocket.
|
|
989
|
+
*
|
|
990
|
+
* @example
|
|
991
|
+
* ```typescript
|
|
992
|
+
* import { joinHallPubsub } from '@soulcraft/sdk/client'
|
|
993
|
+
*
|
|
994
|
+
* const pubsub = await joinHallPubsub({ token, hallUrl: 'wss://hall.soulcraft.com' })
|
|
995
|
+
* pubsub.subscribe('room:abc:chat', { username: 'Alice' })
|
|
996
|
+
* pubsub.on('topicMessage', ({ topic, senderId, payload }) => showMessage(senderId, payload))
|
|
997
|
+
* pubsub.on('presenceUpdate', ({ peerId, action }) => updatePresence(peerId, action))
|
|
998
|
+
*
|
|
999
|
+
* pubsub.broadcast('room:abc:chat', { text: 'Hello!' })
|
|
1000
|
+
* // On leave:
|
|
1001
|
+
* pubsub.close()
|
|
1002
|
+
* ```
|
|
1003
|
+
*/
|
|
1004
|
+
export interface HallPubsubHandle {
|
|
1005
|
+
/** The peer ID for this connection (decoded from the pubsub token). */
|
|
1006
|
+
readonly peerId: string;
|
|
1007
|
+
/**
|
|
1008
|
+
* Subscribe to a pub/sub topic.
|
|
1009
|
+
*
|
|
1010
|
+
* @param topic - Topic name to subscribe to.
|
|
1011
|
+
* @param metadata - Optional presence metadata visible to other subscribers.
|
|
1012
|
+
*/
|
|
1013
|
+
subscribe(topic: string, metadata?: Record<string, unknown>): void;
|
|
1014
|
+
/**
|
|
1015
|
+
* Unsubscribe from a pub/sub topic.
|
|
1016
|
+
*
|
|
1017
|
+
* @param topic - Topic name to unsubscribe from.
|
|
1018
|
+
*/
|
|
1019
|
+
unsubscribe(topic: string): void;
|
|
1020
|
+
/**
|
|
1021
|
+
* Broadcast a message to all subscribers of a topic.
|
|
1022
|
+
*
|
|
1023
|
+
* @param topic - Topic to broadcast to.
|
|
1024
|
+
* @param payload - Arbitrary JSON payload delivered to all subscribers.
|
|
1025
|
+
*/
|
|
1026
|
+
broadcast(topic: string, payload: unknown): void;
|
|
1027
|
+
/**
|
|
1028
|
+
* Request a presence snapshot for a topic.
|
|
1029
|
+
*
|
|
1030
|
+
* @param topic - Topic to query presence for.
|
|
1031
|
+
*/
|
|
1032
|
+
getPresence(topic: string): void;
|
|
1033
|
+
/**
|
|
1034
|
+
* Register a listener for a pub/sub event.
|
|
1035
|
+
*
|
|
1036
|
+
* @param event - The event name.
|
|
1037
|
+
* @param listener - Callback invoked with the typed event payload.
|
|
1038
|
+
*/
|
|
1039
|
+
on<K extends keyof HallPubsubHandleEvents>(event: K, listener: (data: HallPubsubHandleEvents[K]) => void): this;
|
|
1040
|
+
/**
|
|
1041
|
+
* Remove a previously registered listener.
|
|
1042
|
+
*
|
|
1043
|
+
* @param event - The event name.
|
|
1044
|
+
* @param listener - The exact function reference passed to `on`.
|
|
1045
|
+
*/
|
|
1046
|
+
off<K extends keyof HallPubsubHandleEvents>(event: K, listener: (data: HallPubsubHandleEvents[K]) => void): this;
|
|
1047
|
+
/** Disconnect the pub/sub WebSocket. */
|
|
1048
|
+
close(): void;
|
|
1049
|
+
}
|
|
527
1050
|
//# sourceMappingURL=types.d.ts.map
|