@soulcraft/sdk 1.1.0 → 1.3.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.
Files changed (57) hide show
  1. package/dist/client/index.d.ts +3 -0
  2. package/dist/client/index.d.ts.map +1 -1
  3. package/dist/client/index.js +2 -0
  4. package/dist/client/index.js.map +1 -1
  5. package/dist/index.d.ts +1 -1
  6. package/dist/index.d.ts.map +1 -1
  7. package/dist/index.js.map +1 -1
  8. package/dist/modules/auth/service-token.d.ts +62 -0
  9. package/dist/modules/auth/service-token.d.ts.map +1 -0
  10. package/dist/modules/auth/service-token.js +99 -0
  11. package/dist/modules/auth/service-token.js.map +1 -0
  12. package/dist/modules/billing/index.d.ts +17 -5
  13. package/dist/modules/billing/index.d.ts.map +1 -1
  14. package/dist/modules/billing/index.js +24 -9
  15. package/dist/modules/billing/index.js.map +1 -1
  16. package/dist/modules/billing/portal-provider.d.ts +70 -0
  17. package/dist/modules/billing/portal-provider.d.ts.map +1 -0
  18. package/dist/modules/billing/portal-provider.js +204 -0
  19. package/dist/modules/billing/portal-provider.js.map +1 -0
  20. package/dist/modules/billing/types.d.ts +4 -3
  21. package/dist/modules/billing/types.d.ts.map +1 -1
  22. package/dist/modules/billing/types.js +4 -3
  23. package/dist/modules/billing/types.js.map +1 -1
  24. package/dist/modules/hall/browser.d.ts +88 -0
  25. package/dist/modules/hall/browser.d.ts.map +1 -0
  26. package/dist/modules/hall/browser.js +265 -0
  27. package/dist/modules/hall/browser.js.map +1 -0
  28. package/dist/modules/hall/protocol.d.ts +39 -0
  29. package/dist/modules/hall/protocol.d.ts.map +1 -0
  30. package/dist/modules/hall/protocol.js +52 -0
  31. package/dist/modules/hall/protocol.js.map +1 -0
  32. package/dist/modules/hall/server.d.ts +172 -0
  33. package/dist/modules/hall/server.d.ts.map +1 -0
  34. package/dist/modules/hall/server.js +457 -0
  35. package/dist/modules/hall/server.js.map +1 -0
  36. package/dist/modules/hall/types.d.ts +502 -31
  37. package/dist/modules/hall/types.d.ts.map +1 -1
  38. package/dist/modules/hall/types.js +13 -8
  39. package/dist/modules/hall/types.js.map +1 -1
  40. package/dist/modules/license/index.js +2 -2
  41. package/dist/modules/license/index.js.map +1 -1
  42. package/dist/server/from-license.d.ts +213 -0
  43. package/dist/server/from-license.d.ts.map +1 -0
  44. package/dist/server/from-license.js +305 -0
  45. package/dist/server/from-license.js.map +1 -0
  46. package/dist/server/hall-handlers.d.ts +90 -151
  47. package/dist/server/hall-handlers.d.ts.map +1 -1
  48. package/dist/server/hall-handlers.js +84 -204
  49. package/dist/server/hall-handlers.js.map +1 -1
  50. package/dist/server/index.d.ts +6 -2
  51. package/dist/server/index.d.ts.map +1 -1
  52. package/dist/server/index.js +6 -2
  53. package/dist/server/index.js.map +1 -1
  54. package/dist/types.d.ts +22 -18
  55. package/dist/types.d.ts.map +1 -1
  56. package/docs/USAGE.md +224 -1
  57. package/package.json +1 -6
@@ -0,0 +1,204 @@
1
+ /**
2
+ * @module billing/portal-provider
3
+ * @description Portal-backed billing provider for unified cross-product credit tracking.
4
+ *
5
+ * Delegates all credit checks and usage recording to the Portal credit API at
6
+ * `https://portal.soulcraft.com/api/credits/*`. This ensures that a user's
7
+ * 1,000 monthly credits are shared across Workshop, Venue, and Academy — not siloed
8
+ * per product.
9
+ *
10
+ * Activated when `PORTAL_CREDIT_SECRET` is set in the environment. Takes precedence
11
+ * over the Firestore provider.
12
+ *
13
+ * **Endpoint summary:**
14
+ * ```
15
+ * POST /api/credits/check — pre-flight before an AI call
16
+ * POST /api/credits/consume — deduct usage after a single AI response
17
+ * POST /api/credits/batch — flush buffered usage at end of session
18
+ * ```
19
+ *
20
+ * All requests carry `X-Portal-Credit-Secret: <PORTAL_CREDIT_SECRET>`.
21
+ *
22
+ * Subscription data, top-up balances, and usage history are not surfaced via
23
+ * this API — those are managed by Portal directly. Methods that require them
24
+ * (`getUsageStatus`, `getSubscription`, `addTopUp`) return safe defaults.
25
+ * Products that need rich billing UI should call Portal's management API directly.
26
+ *
27
+ * @example
28
+ * ```typescript
29
+ * // Automatically selected when PORTAL_CREDIT_SECRET is set:
30
+ * const billing = createBillingModule()
31
+ *
32
+ * // Explicit override (e.g. for testing):
33
+ * const billing = createBillingModule({
34
+ * provider: new PortalBillingProvider('https://portal.soulcraft.com', secret, 'workshop'),
35
+ * })
36
+ * ```
37
+ */
38
+ // ─────────────────────────────────────────────────────────────────────────────
39
+ // Period helpers
40
+ // ─────────────────────────────────────────────────────────────────────────────
41
+ function _currentPeriod() {
42
+ const d = new Date();
43
+ return `${d.getFullYear()}-${String(d.getMonth() + 1).padStart(2, '0')}`;
44
+ }
45
+ function _thirtyDaysFromNow() {
46
+ return new Date(Date.now() + 30 * 24 * 60 * 60 * 1000).toISOString();
47
+ }
48
+ // ─────────────────────────────────────────────────────────────────────────────
49
+ // Provider
50
+ // ─────────────────────────────────────────────────────────────────────────────
51
+ /**
52
+ * Portal-backed billing provider.
53
+ *
54
+ * Calls the Portal credit API for all gate checks and usage recording.
55
+ * All requests are authenticated with the `X-Portal-Credit-Secret` header.
56
+ *
57
+ * The `userId` parameter on all methods must be the user's **email address** —
58
+ * the same identifier Portal uses to look up the license document.
59
+ */
60
+ export class PortalBillingProvider {
61
+ baseUrl;
62
+ secret;
63
+ product;
64
+ /**
65
+ * @param baseUrl - Portal base URL (e.g. `"https://portal.soulcraft.com"`).
66
+ * @param secret - The value of `PORTAL_CREDIT_SECRET`.
67
+ * @param product - The product name sent with consume/batch calls (`'workshop'`, `'venue'`, `'academy'`).
68
+ */
69
+ constructor(baseUrl, secret, product) {
70
+ this.baseUrl = baseUrl;
71
+ this.secret = secret;
72
+ this.product = product;
73
+ }
74
+ // ── Internal helpers ──────────────────────────────────────────────────────
75
+ _headers() {
76
+ return {
77
+ 'Content-Type': 'application/json',
78
+ 'X-Portal-Credit-Secret': this.secret,
79
+ };
80
+ }
81
+ async _post(path, body) {
82
+ const response = await fetch(`${this.baseUrl}${path}`, {
83
+ method: 'POST',
84
+ headers: this._headers(),
85
+ body: JSON.stringify(body),
86
+ signal: AbortSignal.timeout(10_000),
87
+ });
88
+ if (!response.ok) {
89
+ throw new Error(`Portal credit API error (${response.status}) at ${path}`);
90
+ }
91
+ return response.json();
92
+ }
93
+ // ── BillingProvider interface ─────────────────────────────────────────────
94
+ async checkLimit(userId, hasPersonalKey) {
95
+ if (hasPersonalKey) {
96
+ return { ok: true, remaining: Infinity, topUpBalance: 0, totalAvailable: Infinity, useTopUp: false };
97
+ }
98
+ try {
99
+ const result = await this._post('/api/credits/check', { userEmail: userId, product: this.product });
100
+ return {
101
+ ok: result.ok,
102
+ remaining: result.remaining ?? 0,
103
+ topUpBalance: result.topUpBalance ?? 0,
104
+ totalAvailable: result.totalAvailable ?? 0,
105
+ useTopUp: !result.ok ? false : (result.remaining ?? 1) <= 0,
106
+ ...(!result.ok && result.reason ? { reason: result.reason === 'no_subscription' ? 'no_subscription' : 'limit_reached' } : {}),
107
+ };
108
+ }
109
+ catch {
110
+ // Network failure — fail open to avoid blocking AI calls due to Portal downtime.
111
+ return { ok: true, remaining: 1, topUpBalance: 0, totalAvailable: 1, useTopUp: false };
112
+ }
113
+ }
114
+ async getUsage(userId) {
115
+ // Portal does not expose per-product usage history via the credit API.
116
+ // Return a minimal placeholder — products that need full usage dashboards
117
+ // should call Portal's management API directly.
118
+ void userId;
119
+ return {
120
+ currentPeriod: _currentPeriod(),
121
+ periodEnd: _thirtyDaysFromNow(),
122
+ messagesUsed: 0,
123
+ inputTokens: 0,
124
+ outputTokens: 0,
125
+ byModel: {},
126
+ lastUpdated: new Date().toISOString(),
127
+ };
128
+ }
129
+ async getUsageStatus(userId, hasPersonalKey) {
130
+ const check = await this.checkLimit(userId, hasPersonalKey);
131
+ const usage = await this.getUsage(userId);
132
+ if (hasPersonalKey) {
133
+ return {
134
+ mode: 'byok',
135
+ usage,
136
+ limit: null,
137
+ remaining: null,
138
+ topUpBalance: 0,
139
+ totalAvailable: null,
140
+ percentUsed: null,
141
+ resetsAt: usage.periodEnd,
142
+ estimatedCostUSD: 0,
143
+ };
144
+ }
145
+ return {
146
+ mode: check.ok ? 'included' : 'free',
147
+ usage,
148
+ limit: null, // Portal doesn't expose the plan limit via this API
149
+ remaining: check.remaining === Infinity ? null : check.remaining,
150
+ topUpBalance: check.topUpBalance,
151
+ totalAvailable: check.totalAvailable === Infinity ? null : check.totalAvailable,
152
+ percentUsed: null,
153
+ resetsAt: usage.periodEnd,
154
+ estimatedCostUSD: 0,
155
+ isFreeUser: !check.ok,
156
+ };
157
+ }
158
+ async incrementUsage(userId, inputTokens, outputTokens, model, _useTopUp) {
159
+ try {
160
+ await this._post('/api/credits/consume', {
161
+ userEmail: userId,
162
+ product: this.product,
163
+ inputTokens,
164
+ outputTokens,
165
+ model,
166
+ });
167
+ }
168
+ catch {
169
+ // Fire-and-forget — a consume failure does not block the caller.
170
+ console.error('[SDK/billing] Portal consume failed — usage may be under-counted');
171
+ }
172
+ }
173
+ async batchIncrementUsage(userId, batch) {
174
+ try {
175
+ await this._post('/api/credits/batch', {
176
+ userEmail: userId,
177
+ product: this.product,
178
+ messageCount: batch.messageCount,
179
+ inputTokens: batch.inputTokens,
180
+ outputTokens: batch.outputTokens,
181
+ byModel: batch.byModel,
182
+ });
183
+ }
184
+ catch {
185
+ console.error('[SDK/billing] Portal batch flush failed — usage may be under-counted');
186
+ }
187
+ }
188
+ async getSubscription(_userId) {
189
+ // Subscription data is not exposed via the credit API.
190
+ return undefined;
191
+ }
192
+ async hasActiveSubscription(userId) {
193
+ const check = await this.checkLimit(userId, false);
194
+ return check.ok;
195
+ }
196
+ async getTopUpBalance(userId) {
197
+ const check = await this.checkLimit(userId, false);
198
+ return check.topUpBalance;
199
+ }
200
+ async canAccessPaidFeature(userId) {
201
+ return this.hasActiveSubscription(userId);
202
+ }
203
+ }
204
+ //# sourceMappingURL=portal-provider.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"portal-provider.js","sourceRoot":"","sources":["../../../src/modules/billing/portal-provider.ts"],"names":[],"mappings":"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"}
@@ -10,9 +10,10 @@
10
10
  * - **Top-up credits**: purchase additional credits beyond the monthly allowance
11
11
  * - **Provider abstraction**: local file storage (dev) or Firestore (production)
12
12
  *
13
- * The billing provider is selected automatically based on environment variables:
14
- * - `FIREBASE_SERVICE_ACCOUNT_KEY` set → `FirestoreBillingProvider` (production)
15
- * - Not set → `LocalBillingProvider` (dev, stores state in `.soulcraft/billing/`)
13
+ * The billing provider is selected automatically (priority order):
14
+ * 1. `PORTAL_CREDIT_SECRET` set → `PortalBillingProvider` (cross-product unified credits)
15
+ * 2. `FIREBASE_SERVICE_ACCOUNT_KEY` set → `FirestoreBillingProvider` (per-product production)
16
+ * 3. Neither → `LocalBillingProvider` (dev, stores state in `.soulcraft/billing/`)
16
17
  *
17
18
  * @example Checking limits before an AI call
18
19
  * ```typescript
@@ -1 +1 @@
1
- {"version":3,"file":"types.d.ts","sourceRoot":"","sources":["../../../src/modules/billing/types.ts"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"types.d.ts","sourceRoot":"","sources":["../../../src/modules/billing/types.ts"],"names":[],"mappings":"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"}
@@ -10,9 +10,10 @@
10
10
  * - **Top-up credits**: purchase additional credits beyond the monthly allowance
11
11
  * - **Provider abstraction**: local file storage (dev) or Firestore (production)
12
12
  *
13
- * The billing provider is selected automatically based on environment variables:
14
- * - `FIREBASE_SERVICE_ACCOUNT_KEY` set → `FirestoreBillingProvider` (production)
15
- * - Not set → `LocalBillingProvider` (dev, stores state in `.soulcraft/billing/`)
13
+ * The billing provider is selected automatically (priority order):
14
+ * 1. `PORTAL_CREDIT_SECRET` set → `PortalBillingProvider` (cross-product unified credits)
15
+ * 2. `FIREBASE_SERVICE_ACCOUNT_KEY` set → `FirestoreBillingProvider` (per-product production)
16
+ * 3. Neither → `LocalBillingProvider` (dev, stores state in `.soulcraft/billing/`)
16
17
  *
17
18
  * @example Checking limits before an AI call
18
19
  * ```typescript
@@ -1 +1 @@
1
- {"version":3,"file":"types.js","sourceRoot":"","sources":["../../../src/modules/billing/types.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;;GAuBG"}
1
+ {"version":3,"file":"types.js","sourceRoot":"","sources":["../../../src/modules/billing/types.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;;;GAwBG"}
@@ -0,0 +1,88 @@
1
+ /**
2
+ * @module modules/hall/browser
3
+ * @description Browser-side Hall room client — joins a WebRTC room via a session token.
4
+ *
5
+ * `joinHallRoom` is the browser entry point for Hall real-time communication.
6
+ * It opens a WebSocket to the Hall signaling endpoint, negotiates a WebRTC peer
7
+ * connection via offer/answer/ICE, and returns a `HallRoomHandle` with typed events
8
+ * for tracks, transcripts, concept mentions, and room lifecycle.
9
+ *
10
+ * **Auth model:** The browser never sees the product's shared secret. The product
11
+ * backend mints a short-lived HMAC session token (via `sdk.hall.createSessionToken`)
12
+ * and passes it to the browser. The browser passes the token to `joinHallRoom` — that
13
+ * is the only browser credential.
14
+ *
15
+ * This module has no server-only dependencies and is safe to bundle for the browser.
16
+ * It does require `RTCPeerConnection` and `WebSocket` to be available (standard in
17
+ * all modern browsers; not available in Node.js without polyfills).
18
+ *
19
+ * @example
20
+ * ```typescript
21
+ * import { joinHallRoom } from '@soulcraft/sdk/client'
22
+ *
23
+ * // 1. Get token from product backend:
24
+ * const { token, hallUrl } = await fetch('/api/session/join').then(r => r.json())
25
+ *
26
+ * // 2. Get camera + mic:
27
+ * const stream = await navigator.mediaDevices.getUserMedia({ video: true, audio: true })
28
+ *
29
+ * // 3. Join room:
30
+ * const room = await joinHallRoom({ token, hallUrl })
31
+ * room.addStream(stream)
32
+ *
33
+ * // 4. Handle events:
34
+ * room.on('trackAdded', ({ peerId, streams }) => {
35
+ * document.getElementById(`video-${peerId}`).srcObject = streams[0]
36
+ * })
37
+ * room.on('transcript', ({ peerId, text, isFinal }) => {
38
+ * if (isFinal) appendCaption(peerId, text)
39
+ * })
40
+ * room.on('conceptMention', ({ nodeId, confidence }) => pulseGraphNode(nodeId, confidence))
41
+ *
42
+ * // 5. On leave:
43
+ * room.close()
44
+ * ```
45
+ */
46
+ import type { HallRoomHandle } from './types.js';
47
+ /**
48
+ * Options for {@link joinHallRoom}.
49
+ */
50
+ export interface JoinHallRoomOptions {
51
+ /**
52
+ * The session token issued by the product backend via `sdk.hall.createSessionToken()`.
53
+ * Embedded in the Hall WebSocket URL — never sent as a header or query param separately.
54
+ */
55
+ token: string;
56
+ /**
57
+ * The Hall server WebSocket base URL (e.g. `"wss://hall.soulcraft.com"`).
58
+ * Provided by the product backend alongside the token.
59
+ */
60
+ hallUrl: string;
61
+ /**
62
+ * Optional RTCConfiguration overrides (e.g. additional STUN/TURN servers).
63
+ * Hall's in-process TURN server is always included automatically via the signaling handshake.
64
+ * Pass additional servers here if you have a separate STUN/TURN setup.
65
+ */
66
+ iceServers?: RTCIceServer[];
67
+ }
68
+ /**
69
+ * Join a Hall room from the browser using a session token.
70
+ *
71
+ * Opens a WebSocket to `{hallUrl}/ws/session/{token}`, creates an `RTCPeerConnection`,
72
+ * performs the offer/answer/ICE exchange, and resolves once the peer connection reaches
73
+ * `connected` state. Returns a {@link HallRoomHandle} with typed events.
74
+ *
75
+ * @param options - Token, Hall URL, and optional ICE server overrides.
76
+ * @returns A connected `HallRoomHandle`.
77
+ * @throws {Error} If the signaling handshake fails or the connection times out.
78
+ *
79
+ * @example
80
+ * ```typescript
81
+ * const room = await joinHallRoom({ token, hallUrl: 'wss://hall.soulcraft.com' })
82
+ * room.addStream(localStream)
83
+ * room.on('trackAdded', ({ peerId, streams }) => renderVideo(peerId, streams[0]))
84
+ * room.on('transcript', ({ text, isFinal }) => { if (isFinal) showCaption(text) })
85
+ * ```
86
+ */
87
+ export declare function joinHallRoom(options: JoinHallRoomOptions): Promise<HallRoomHandle>;
88
+ //# sourceMappingURL=browser.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"browser.d.ts","sourceRoot":"","sources":["../../../src/modules/hall/browser.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GA4CG;AAEH,OAAO,KAAK,EACV,cAAc,EAQf,MAAM,YAAY,CAAA;AAoBnB;;GAEG;AACH,MAAM,WAAW,mBAAmB;IAClC;;;OAGG;IACH,KAAK,EAAE,MAAM,CAAA;IACb;;;OAGG;IACH,OAAO,EAAE,MAAM,CAAA;IACf;;;;OAIG;IACH,UAAU,CAAC,EAAE,YAAY,EAAE,CAAA;CAC5B;AAgFD;;;;;;;;;;;;;;;;;;GAkBG;AACH,wBAAsB,YAAY,CAAC,OAAO,EAAE,mBAAmB,GAAG,OAAO,CAAC,cAAc,CAAC,CAgIxF"}
@@ -0,0 +1,265 @@
1
+ /**
2
+ * @module modules/hall/browser
3
+ * @description Browser-side Hall room client — joins a WebRTC room via a session token.
4
+ *
5
+ * `joinHallRoom` is the browser entry point for Hall real-time communication.
6
+ * It opens a WebSocket to the Hall signaling endpoint, negotiates a WebRTC peer
7
+ * connection via offer/answer/ICE, and returns a `HallRoomHandle` with typed events
8
+ * for tracks, transcripts, concept mentions, and room lifecycle.
9
+ *
10
+ * **Auth model:** The browser never sees the product's shared secret. The product
11
+ * backend mints a short-lived HMAC session token (via `sdk.hall.createSessionToken`)
12
+ * and passes it to the browser. The browser passes the token to `joinHallRoom` — that
13
+ * is the only browser credential.
14
+ *
15
+ * This module has no server-only dependencies and is safe to bundle for the browser.
16
+ * It does require `RTCPeerConnection` and `WebSocket` to be available (standard in
17
+ * all modern browsers; not available in Node.js without polyfills).
18
+ *
19
+ * @example
20
+ * ```typescript
21
+ * import { joinHallRoom } from '@soulcraft/sdk/client'
22
+ *
23
+ * // 1. Get token from product backend:
24
+ * const { token, hallUrl } = await fetch('/api/session/join').then(r => r.json())
25
+ *
26
+ * // 2. Get camera + mic:
27
+ * const stream = await navigator.mediaDevices.getUserMedia({ video: true, audio: true })
28
+ *
29
+ * // 3. Join room:
30
+ * const room = await joinHallRoom({ token, hallUrl })
31
+ * room.addStream(stream)
32
+ *
33
+ * // 4. Handle events:
34
+ * room.on('trackAdded', ({ peerId, streams }) => {
35
+ * document.getElementById(`video-${peerId}`).srcObject = streams[0]
36
+ * })
37
+ * room.on('transcript', ({ peerId, text, isFinal }) => {
38
+ * if (isFinal) appendCaption(peerId, text)
39
+ * })
40
+ * room.on('conceptMention', ({ nodeId, confidence }) => pulseGraphNode(nodeId, confidence))
41
+ *
42
+ * // 5. On leave:
43
+ * room.close()
44
+ * ```
45
+ */
46
+ class HallRoomHandleImpl {
47
+ roomId;
48
+ peerId;
49
+ #listeners = {
50
+ transcript: new Set(),
51
+ conceptMention: new Set(),
52
+ relationProposed: new Set(),
53
+ speakerChanged: new Set(),
54
+ peerJoined: new Set(),
55
+ peerLeft: new Set(),
56
+ trackAdded: new Set(),
57
+ trackRemoved: new Set(),
58
+ closed: new Set(),
59
+ error: new Set(),
60
+ };
61
+ #pc;
62
+ #ws;
63
+ constructor(roomId, peerId, pc, ws) {
64
+ this.roomId = roomId;
65
+ this.peerId = peerId;
66
+ this.#pc = pc;
67
+ this.#ws = ws;
68
+ }
69
+ on(event, listener) {
70
+ ;
71
+ this.#listeners[event].add(listener);
72
+ return this;
73
+ }
74
+ off(event, listener) {
75
+ ;
76
+ this.#listeners[event].delete(listener);
77
+ return this;
78
+ }
79
+ addStream(stream) {
80
+ for (const track of stream.getTracks()) {
81
+ this.#pc.addTrack(track, stream);
82
+ }
83
+ }
84
+ close() {
85
+ this.#ws.close(1000, 'peer leaving');
86
+ this.#pc.close();
87
+ }
88
+ /** @internal */
89
+ _dispatch(event, payload) {
90
+ for (const listener of this.#listeners[event]) {
91
+ try {
92
+ listener(payload);
93
+ }
94
+ catch (e) {
95
+ console.error(`[Hall] uncaught error in room '${this.roomId}' ${event} listener:`, e);
96
+ }
97
+ }
98
+ }
99
+ }
100
+ // ─── joinHallRoom ─────────────────────────────────────────────────────────────
101
+ /**
102
+ * Join a Hall room from the browser using a session token.
103
+ *
104
+ * Opens a WebSocket to `{hallUrl}/ws/session/{token}`, creates an `RTCPeerConnection`,
105
+ * performs the offer/answer/ICE exchange, and resolves once the peer connection reaches
106
+ * `connected` state. Returns a {@link HallRoomHandle} with typed events.
107
+ *
108
+ * @param options - Token, Hall URL, and optional ICE server overrides.
109
+ * @returns A connected `HallRoomHandle`.
110
+ * @throws {Error} If the signaling handshake fails or the connection times out.
111
+ *
112
+ * @example
113
+ * ```typescript
114
+ * const room = await joinHallRoom({ token, hallUrl: 'wss://hall.soulcraft.com' })
115
+ * room.addStream(localStream)
116
+ * room.on('trackAdded', ({ peerId, streams }) => renderVideo(peerId, streams[0]))
117
+ * room.on('transcript', ({ text, isFinal }) => { if (isFinal) showCaption(text) })
118
+ * ```
119
+ */
120
+ export async function joinHallRoom(options) {
121
+ const { token, hallUrl, iceServers = [] } = options;
122
+ const signalingUrl = `${hallUrl}/ws/session/${encodeURIComponent(token)}`;
123
+ // The Hall server sends TURN credentials in the first message after the WebSocket opens.
124
+ // We resolve ICE servers from that before creating the RTCPeerConnection.
125
+ const ws = new WebSocket(signalingUrl);
126
+ const handle = await new Promise((resolve, reject) => {
127
+ const timeout = setTimeout(() => {
128
+ ws.close();
129
+ reject(new Error('Hall: signaling connection timed out after 30s'));
130
+ }, 30_000);
131
+ ws.onerror = () => {
132
+ clearTimeout(timeout);
133
+ reject(new Error('Hall: WebSocket error during signaling'));
134
+ };
135
+ ws.onopen = async () => {
136
+ // Create the peer connection. Hall's TURN server handles NAT traversal —
137
+ // the signaling 'answer' message from Hall includes ICE candidates.
138
+ const pc = new RTCPeerConnection({
139
+ iceServers: [
140
+ // Hall's in-process TURN is always available.
141
+ // The session token encodes the TURN credentials — Hall provides them via the ws.
142
+ { urls: `${hallUrl.replace('wss://', 'turn:').replace('ws://', 'turn:')}:3478` },
143
+ ...iceServers,
144
+ ],
145
+ });
146
+ // Send ICE candidates to Hall as they are gathered.
147
+ pc.onicecandidate = (ev) => {
148
+ if (ev.candidate) {
149
+ ws.send(JSON.stringify({ type: 'ice', candidate: JSON.stringify(ev.candidate) }));
150
+ }
151
+ };
152
+ // Create and send SDP offer.
153
+ const offer = await pc.createOffer();
154
+ await pc.setLocalDescription(offer);
155
+ ws.send(JSON.stringify({ type: 'offer', sdp: offer.sdp }));
156
+ ws.onmessage = async (ev) => {
157
+ let msg;
158
+ try {
159
+ msg = JSON.parse(ev.data);
160
+ }
161
+ catch {
162
+ return;
163
+ }
164
+ if (msg.type === 'answer') {
165
+ await pc.setRemoteDescription({ type: 'answer', sdp: msg.sdp });
166
+ return;
167
+ }
168
+ if (msg.type === 'ice') {
169
+ try {
170
+ await pc.addIceCandidate(JSON.parse(msg.candidate));
171
+ }
172
+ catch {
173
+ // Stale ICE candidates are harmless to ignore.
174
+ }
175
+ return;
176
+ }
177
+ if (msg.type === 'error') {
178
+ clearTimeout(timeout);
179
+ ws.close();
180
+ pc.close();
181
+ reject(new Error(`Hall signaling error: ${msg.message}`));
182
+ return;
183
+ }
184
+ // The peer connection is considered "ready" once we have a remote description.
185
+ // Resolve the handle at this point so event listeners can be attached.
186
+ // We defer the roomId/peerId extraction to after the connection resolves.
187
+ // Hall sends peerJoined as the first room event after ICE completes.
188
+ if (!roomHandleRef)
189
+ return;
190
+ routeSignalingEvent(roomHandleRef, msg);
191
+ };
192
+ // Resolve once connected. Track events before connection is established are
193
+ // buffered by the browser's RTCPeerConnection — addTrack after setRemoteDescription.
194
+ pc.onconnectionstatechange = () => {
195
+ if (pc.connectionState === 'connected' && !roomHandleRef) {
196
+ // roomId and peerId are decoded from the token by Hall and sent in peerJoined.
197
+ // We use placeholders here; they are filled in by the first peerJoined event.
198
+ const h = new HallRoomHandleImpl('', '', pc, ws);
199
+ roomHandleRef = h;
200
+ clearTimeout(timeout);
201
+ resolve(h);
202
+ }
203
+ if (pc.connectionState === 'failed' || pc.connectionState === 'closed') {
204
+ if (!roomHandleRef) {
205
+ clearTimeout(timeout);
206
+ reject(new Error(`Hall: WebRTC connection ${pc.connectionState}`));
207
+ }
208
+ }
209
+ };
210
+ // Wire track events.
211
+ pc.ontrack = (ev) => {
212
+ if (!roomHandleRef)
213
+ return;
214
+ roomHandleRef._dispatch('trackAdded', {
215
+ peerId: 'remote', // refined by peerJoined
216
+ track: ev.track,
217
+ streams: ev.streams,
218
+ });
219
+ ev.track.onended = () => {
220
+ roomHandleRef?._dispatch('trackRemoved', { peerId: 'remote', track: ev.track });
221
+ };
222
+ };
223
+ };
224
+ let roomHandleRef = null;
225
+ ws.onclose = (ev) => {
226
+ if (roomHandleRef) {
227
+ if (ev.code !== 1000) {
228
+ roomHandleRef._dispatch('error', { message: `Hall WebSocket closed: ${ev.reason || ev.code}` });
229
+ }
230
+ roomHandleRef._dispatch('closed', { roomId: roomHandleRef.roomId });
231
+ }
232
+ };
233
+ });
234
+ return handle;
235
+ }
236
+ // ─── Signaling event router ───────────────────────────────────────────────────
237
+ function routeSignalingEvent(handle, msg) {
238
+ switch (msg.type) {
239
+ case 'transcript':
240
+ handle._dispatch('transcript', msg);
241
+ break;
242
+ case 'conceptMention':
243
+ handle._dispatch('conceptMention', msg);
244
+ break;
245
+ case 'relationProposed':
246
+ handle._dispatch('relationProposed', msg);
247
+ break;
248
+ case 'speaker_changed':
249
+ handle._dispatch('speakerChanged', { roomId: msg.roomId, peerId: msg.speakerId });
250
+ break;
251
+ case 'peerJoined':
252
+ handle._dispatch('peerJoined', msg);
253
+ break;
254
+ case 'peerLeft':
255
+ handle._dispatch('peerLeft', msg);
256
+ break;
257
+ case 'roomClosed':
258
+ handle._dispatch('closed', { roomId: msg.roomId });
259
+ break;
260
+ case 'error':
261
+ handle._dispatch('error', { message: msg.message });
262
+ break;
263
+ }
264
+ }
265
+ //# sourceMappingURL=browser.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"browser.js","sourceRoot":"","sources":["../../../src/modules/hall/browser.ts"],"names":[],"mappings":"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"}