@tribe-nest/media-client 0.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.
Files changed (65) hide show
  1. package/README.md +68 -0
  2. package/build/core/index.d.ts +17 -0
  3. package/build/core/index.d.ts.map +1 -0
  4. package/build/core/index.js +41 -0
  5. package/build/core/index.js.map +1 -0
  6. package/build/core/reconnect.d.ts +95 -0
  7. package/build/core/reconnect.d.ts.map +1 -0
  8. package/build/core/reconnect.js +160 -0
  9. package/build/core/reconnect.js.map +1 -0
  10. package/build/core/signal.d.ts +184 -0
  11. package/build/core/signal.d.ts.map +1 -0
  12. package/build/core/signal.js +416 -0
  13. package/build/core/signal.js.map +1 -0
  14. package/build/core/socket.d.ts +57 -0
  15. package/build/core/socket.d.ts.map +1 -0
  16. package/build/core/socket.js +37 -0
  17. package/build/core/socket.js.map +1 -0
  18. package/build/core/state.d.ts +67 -0
  19. package/build/core/state.d.ts.map +1 -0
  20. package/build/core/state.js +193 -0
  21. package/build/core/state.js.map +1 -0
  22. package/build/index.d.ts +29 -0
  23. package/build/index.d.ts.map +1 -0
  24. package/build/index.js +51 -0
  25. package/build/index.js.map +1 -0
  26. package/build/protocol.d.ts +10 -0
  27. package/build/protocol.d.ts.map +1 -0
  28. package/build/protocol.js +26 -0
  29. package/build/protocol.js.map +1 -0
  30. package/build/react/index.d.ts +147 -0
  31. package/build/react/index.d.ts.map +1 -0
  32. package/build/react/index.js +319 -0
  33. package/build/react/index.js.map +1 -0
  34. package/build/room/browserDevice.d.ts +3 -0
  35. package/build/room/browserDevice.d.ts.map +1 -0
  36. package/build/room/browserDevice.js +94 -0
  37. package/build/room/browserDevice.js.map +1 -0
  38. package/build/room/device.d.ts +114 -0
  39. package/build/room/device.d.ts.map +1 -0
  40. package/build/room/device.js +3 -0
  41. package/build/room/device.js.map +1 -0
  42. package/build/room/room.d.ts +219 -0
  43. package/build/room/room.d.ts.map +1 -0
  44. package/build/room/room.js +438 -0
  45. package/build/room/room.js.map +1 -0
  46. package/package.json +69 -0
  47. package/src/_tests/clientBoundary.spec.ts +110 -0
  48. package/src/core/_tests/coreBoundary.spec.ts +70 -0
  49. package/src/core/_tests/fakeSignalServer.ts +188 -0
  50. package/src/core/_tests/reconnect.spec.ts +180 -0
  51. package/src/core/_tests/signal.spec.ts +347 -0
  52. package/src/core/_tests/state.spec.ts +226 -0
  53. package/src/core/index.ts +63 -0
  54. package/src/core/reconnect.ts +233 -0
  55. package/src/core/signal.ts +527 -0
  56. package/src/core/socket.ts +58 -0
  57. package/src/core/state.ts +251 -0
  58. package/src/index.ts +54 -0
  59. package/src/protocol.ts +9 -0
  60. package/src/react/_tests/hooks.spec.tsx +509 -0
  61. package/src/react/index.tsx +439 -0
  62. package/src/room/_tests/room.spec.ts +595 -0
  63. package/src/room/browserDevice.ts +114 -0
  64. package/src/room/device.ts +119 -0
  65. package/src/room/room.ts +600 -0
@@ -0,0 +1,416 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.MediaSignal = void 0;
4
+ exports.assertTokenNotInUrl = assertTokenNotInUrl;
5
+ exports.causeFromError = causeFromError;
6
+ const media_protocol_1 = require("@tribe-nest/media-protocol");
7
+ const socket_1 = require("./socket");
8
+ const DEFAULT_REQUEST_TIMEOUT_MS = 10_000;
9
+ const DEFAULT_JOIN_TIMEOUT_MS = 10_000;
10
+ /**
11
+ * A join ticket in a URL is a join ticket in every access log between the
12
+ * client and the node, and load balancers log query strings by default. The
13
+ * contract puts the token in the first frame; this refuses a URL that looks
14
+ * like someone routed around that.
15
+ */
16
+ function assertTokenNotInUrl(mediaUrl) {
17
+ let url;
18
+ try {
19
+ url = new URL(mediaUrl);
20
+ }
21
+ catch {
22
+ throw new Error(`mediaUrl is not a URL: ${mediaUrl}`);
23
+ }
24
+ for (const key of url.searchParams.keys()) {
25
+ if (/token|jwt|ticket|auth/i.test(key)) {
26
+ throw new Error(`refusing to connect: mediaUrl carries "${key}" in the query string. The token goes in the first frame.`);
27
+ }
28
+ }
29
+ }
30
+ /** Maps a thrown error back to the cause the reconnect policy decides on. */
31
+ function causeFromError(error) {
32
+ if (error instanceof media_protocol_1.MediaError) {
33
+ return { type: "refused", code: error.code, message: error.message };
34
+ }
35
+ return { type: "socket_closed", reason: error instanceof Error ? error.message : String(error) };
36
+ }
37
+ class MediaSignal {
38
+ options;
39
+ socket = null;
40
+ nextId = 0;
41
+ pending = new Map();
42
+ handlers = new Map();
43
+ anyHandlers = new Set();
44
+ closeHandlers = new Set();
45
+ /** Set by the events that explain a close BEFORE the socket goes away. */
46
+ terminalCause = null;
47
+ phaseValue = "idle";
48
+ identityValue = null;
49
+ joinedResolve = null;
50
+ connectReject = null;
51
+ /**
52
+ * Which connection attempt is the current one.
53
+ *
54
+ * `connect()` awaits `getCredentials()` before it has a socket, and until that
55
+ * socket exists there is nothing for `close()` to close: it flips the phase
56
+ * and returns. So the attempt is stamped and re-checked, and an attempt that
57
+ * is no longer the current one abandons itself instead of opening a socket
58
+ * and JOINING with a live token that nothing then holds a reference to.
59
+ */
60
+ attempt = 0;
61
+ boundClose = null;
62
+ constructor(options) {
63
+ this.options = options;
64
+ }
65
+ get phase() {
66
+ return this.phaseValue;
67
+ }
68
+ /** Our own identity, as the node reported it in `joined`. */
69
+ get identity() {
70
+ return this.identityValue;
71
+ }
72
+ /**
73
+ * Opens the socket, sends `join` as the first frame, and resolves once BOTH
74
+ * the join reply and the `joined` event have arrived.
75
+ *
76
+ * Waiting for both is deliberate. The reply says the node accepted the token;
77
+ * the event carries `routerRtpCapabilities`, the peer snapshot and the
78
+ * recording flag. A caller that resolved on the reply alone would be handed a
79
+ * room it knows nothing about, and the contract fixes no order between the
80
+ * two frames, so neither may be assumed to arrive first.
81
+ */
82
+ async connect() {
83
+ if (this.phaseValue === "connecting" || this.phaseValue === "joining" || this.phaseValue === "joined") {
84
+ throw new Error("connect() called on a signal that is already connecting or connected");
85
+ }
86
+ this.resetForConnect();
87
+ const attempt = ++this.attempt;
88
+ this.phaseValue = "connecting";
89
+ const credentials = await this.options.getCredentials();
90
+ // Fetching a ticket is a network round trip of its own, so this window is
91
+ // seconds wide and a caller giving up inside it is ordinary. Every later
92
+ // step has a socket and is cancelled by `handleClose`; this one is the only
93
+ // part of a connect that a `close()` could not reach.
94
+ this.assertAttemptIsCurrent(attempt);
95
+ assertTokenNotInUrl(credentials.mediaUrl);
96
+ const factory = this.options.webSocket ?? socket_1.defaultWebSocketFactory;
97
+ const socket = factory(credentials.mediaUrl);
98
+ this.socket = socket;
99
+ this.bind(socket);
100
+ try {
101
+ await this.waitForOpen(socket);
102
+ this.phaseValue = "joining";
103
+ const joined = await this.performJoin(credentials.token);
104
+ this.phaseValue = "joined";
105
+ this.identityValue = joined.identity;
106
+ return joined;
107
+ }
108
+ catch (error) {
109
+ // A failed handshake leaves nothing worth keeping open, and the cause is
110
+ // recorded first so close handlers see the refusal rather than a bare
111
+ // socket close.
112
+ if (!this.terminalCause)
113
+ this.terminalCause = causeFromError(error);
114
+ this.closeSocket();
115
+ throw error;
116
+ }
117
+ }
118
+ /**
119
+ * Sends a request and resolves with its `data`.
120
+ *
121
+ * `unknown`, not a guessed shape: the wire declares `data: unknown` because
122
+ * most of it is mediasoup's, and a client that pretends otherwise is asserting
123
+ * a contract this package does not hold.
124
+ */
125
+ request(frame, timeoutMs) {
126
+ const socket = this.socket;
127
+ if (!socket || this.phaseValue === "closed" || this.phaseValue === "idle") {
128
+ return Promise.reject(new media_protocol_1.MediaError("internal", `cannot send ${frame.method}: the signal connection is ${this.phaseValue}`));
129
+ }
130
+ const id = this.nextId++;
131
+ const promise = this.track(id, frame.method, timeoutMs ?? this.options.requestTimeoutMs ?? DEFAULT_REQUEST_TIMEOUT_MS);
132
+ try {
133
+ this.send({ ...frame, id });
134
+ }
135
+ catch (error) {
136
+ // A socket that died between the check and the send must reject this
137
+ // call, not throw past a `.catch()` the caller reasonably attached.
138
+ this.settle(id, error);
139
+ }
140
+ return promise;
141
+ }
142
+ /**
143
+ * Best-effort `leave` then close.
144
+ *
145
+ * Best-effort because the node tears the session down on socket close anyway.
146
+ * A leave that hangs must not stop a user closing a tab.
147
+ */
148
+ async leave() {
149
+ if (this.phaseValue === "joined") {
150
+ try {
151
+ await this.request({ method: "leave" });
152
+ }
153
+ catch (error) {
154
+ this.log("debug", "leave was not acknowledged; closing anyway", error);
155
+ }
156
+ }
157
+ this.close();
158
+ }
159
+ /** Closes the socket. Idempotent. */
160
+ close() {
161
+ if (!this.terminalCause)
162
+ this.terminalCause = { type: "closed_by_client" };
163
+ this.closeSocket();
164
+ }
165
+ on(event, handler) {
166
+ const set = this.handlers.get(event) ?? new Set();
167
+ const erased = handler;
168
+ set.add(erased);
169
+ this.handlers.set(event, set);
170
+ return () => {
171
+ set.delete(erased);
172
+ };
173
+ }
174
+ /** Every event, in arrival order. This is what the state reducer is fed. */
175
+ onAny(handler) {
176
+ this.anyHandlers.add(handler);
177
+ return () => {
178
+ this.anyHandlers.delete(handler);
179
+ };
180
+ }
181
+ onClose(handler) {
182
+ this.closeHandlers.add(handler);
183
+ return () => {
184
+ this.closeHandlers.delete(handler);
185
+ };
186
+ }
187
+ // ---------------------------------------------------------------- internals
188
+ /**
189
+ * Throws when this attempt has been overtaken: either `close()` ran, or a
190
+ * second `connect()` started (which the entry guard permits once the phase is
191
+ * `closed`, so an abandoned attempt must not go on to open a second socket).
192
+ */
193
+ assertAttemptIsCurrent(attempt) {
194
+ if (attempt === this.attempt && this.phaseValue !== "closed")
195
+ return;
196
+ throw new media_protocol_1.MediaError("internal", "connect() was abandoned: the connection was closed before the socket was opened");
197
+ }
198
+ resetForConnect() {
199
+ this.terminalCause = null;
200
+ this.identityValue = null;
201
+ this.joinedResolve = null;
202
+ this.connectReject = null;
203
+ this.pending.clear();
204
+ this.nextId = 0;
205
+ }
206
+ bind(socket) {
207
+ socket.addEventListener("message", (event) => this.handleMessage(event.data));
208
+ socket.addEventListener("error", (event) => this.log("warn", "signal socket error", event.error));
209
+ const onClose = (event) => this.handleClose(event);
210
+ this.boundClose = onClose;
211
+ socket.addEventListener("close", onClose);
212
+ }
213
+ waitForOpen(socket) {
214
+ if (socket.readyState === socket_1.SOCKET_OPEN)
215
+ return Promise.resolve();
216
+ return new Promise((resolve, reject) => {
217
+ this.connectReject = reject;
218
+ const onOpen = () => {
219
+ socket.removeEventListener("open", onOpen);
220
+ this.connectReject = null;
221
+ resolve();
222
+ };
223
+ socket.addEventListener("open", onOpen);
224
+ });
225
+ }
226
+ async performJoin(token) {
227
+ const id = this.nextId++;
228
+ const timeoutMs = this.options.joinTimeoutMs ?? DEFAULT_JOIN_TIMEOUT_MS;
229
+ const reply = this.track(id, "join", timeoutMs);
230
+ const joined = new Promise((resolve, reject) => {
231
+ this.joinedResolve = resolve;
232
+ // The same rejection path as the reply: a socket that dies mid-handshake
233
+ // must not leave `connect()` hanging until the join timeout.
234
+ const previous = this.connectReject;
235
+ this.connectReject = (error) => {
236
+ previous?.(error);
237
+ reject(error);
238
+ };
239
+ });
240
+ this.send({
241
+ method: "join",
242
+ id,
243
+ protocolVersion: this.options.protocolVersion ?? media_protocol_1.MEDIA_PROTOCOL_VERSION,
244
+ token,
245
+ });
246
+ const [, joinedFrame] = await Promise.all([reply, this.withDeadline(joined, timeoutMs, "joined")]);
247
+ return joinedFrame;
248
+ }
249
+ withDeadline(promise, ms, what) {
250
+ return new Promise((resolve, reject) => {
251
+ const timer = setTimeout(() => {
252
+ // No `timeout` code exists in the frozen error union, so this reports
253
+ // `internal` and says what timed out in the message.
254
+ reject(new media_protocol_1.MediaError("internal", `timed out waiting for ${what} after ${ms}ms`));
255
+ }, ms);
256
+ promise.then((value) => {
257
+ clearTimeout(timer);
258
+ resolve(value);
259
+ }, (error) => {
260
+ clearTimeout(timer);
261
+ reject(error);
262
+ });
263
+ });
264
+ }
265
+ track(id, method, timeoutMs) {
266
+ return new Promise((resolve, reject) => {
267
+ const timer = setTimeout(() => {
268
+ this.pending.delete(id);
269
+ reject(new media_protocol_1.MediaError("internal", `no reply to ${method} (id ${id}) after ${timeoutMs}ms`));
270
+ }, timeoutMs);
271
+ this.pending.set(id, { resolve, reject, timer, method });
272
+ });
273
+ }
274
+ settle(id, error) {
275
+ const pending = this.pending.get(id);
276
+ if (!pending)
277
+ return;
278
+ this.pending.delete(id);
279
+ if (pending.timer)
280
+ clearTimeout(pending.timer);
281
+ pending.reject(error);
282
+ }
283
+ send(frame) {
284
+ const socket = this.socket;
285
+ if (!socket)
286
+ throw new media_protocol_1.MediaError("internal", "no socket");
287
+ socket.send(JSON.stringify(frame));
288
+ }
289
+ handleMessage(data) {
290
+ const text = toText(data);
291
+ if (text === null) {
292
+ this.log("warn", "dropping a non-text signal frame");
293
+ return;
294
+ }
295
+ let raw;
296
+ try {
297
+ raw = JSON.parse(text);
298
+ }
299
+ catch {
300
+ this.log("warn", "dropping an unparseable signal frame");
301
+ return;
302
+ }
303
+ const parsed = media_protocol_1.serverFrameSchema.safeParse(raw);
304
+ if (!parsed.success) {
305
+ // Dropped, not thrown: a node that gains a frame in a later protocol
306
+ // version must not kill an older client's live call. A real mismatch is
307
+ // caught by the version handshake, loudly, at join.
308
+ this.log("warn", "dropping a frame that does not match the protocol", raw);
309
+ return;
310
+ }
311
+ this.dispatch(parsed.data);
312
+ }
313
+ dispatch(frame) {
314
+ if ((0, media_protocol_1.isEvent)(frame)) {
315
+ this.handleEvent(frame);
316
+ return;
317
+ }
318
+ const pending = this.pending.get(frame.id);
319
+ if (!pending) {
320
+ // Either a second reply to one id or a reply to a request that already
321
+ // timed out. Both are protocol violations by the node, so they are said
322
+ // out loud rather than absorbed.
323
+ this.log("warn", `reply for unknown request id ${frame.id}`, frame);
324
+ return;
325
+ }
326
+ this.pending.delete(frame.id);
327
+ if (pending.timer)
328
+ clearTimeout(pending.timer);
329
+ if (frame.ok) {
330
+ pending.resolve(frame.data);
331
+ }
332
+ else {
333
+ pending.reject(new media_protocol_1.MediaError(frame.code, frame.message ?? `${pending.method} refused: ${frame.code}`));
334
+ }
335
+ }
336
+ handleEvent(frame) {
337
+ switch (frame.event) {
338
+ case "joined":
339
+ this.identityValue = frame.identity;
340
+ this.joinedResolve?.(frame);
341
+ this.joinedResolve = null;
342
+ break;
343
+ case "draining":
344
+ // Recorded now because the node closes the socket next, and by then the
345
+ // reason is gone. Reconnecting to this node is precisely wrong.
346
+ this.terminalCause = { type: "draining", reconnectAfterMs: frame.reconnectAfterMs };
347
+ break;
348
+ case "roomClosed":
349
+ this.terminalCause = { type: "room_closed", reason: frame.reason };
350
+ break;
351
+ default:
352
+ break;
353
+ }
354
+ for (const handler of this.handlers.get(frame.event) ?? [])
355
+ handler(frame);
356
+ for (const handler of this.anyHandlers)
357
+ handler(frame);
358
+ }
359
+ handleClose(event) {
360
+ if (this.phaseValue === "closed")
361
+ return;
362
+ this.phaseValue = "closed";
363
+ const cause = this.terminalCause ?? {
364
+ type: "socket_closed",
365
+ ...(event.code === undefined ? {} : { code: event.code }),
366
+ ...(event.reason === undefined ? {} : { reason: event.reason }),
367
+ };
368
+ const closedError = new media_protocol_1.MediaError("internal", `signal connection closed before a reply arrived (${cause.type})`);
369
+ for (const [, pending] of this.pending) {
370
+ if (pending.timer)
371
+ clearTimeout(pending.timer);
372
+ pending.reject(closedError);
373
+ }
374
+ this.pending.clear();
375
+ this.connectReject?.(closedError);
376
+ this.connectReject = null;
377
+ this.joinedResolve = null;
378
+ for (const handler of this.closeHandlers)
379
+ handler(cause);
380
+ }
381
+ closeSocket() {
382
+ const socket = this.socket;
383
+ if (!socket) {
384
+ // Nothing was ever opened, so no close event will arrive to settle
385
+ // pending work. Settle it here.
386
+ if (this.phaseValue !== "closed")
387
+ this.handleClose({});
388
+ return;
389
+ }
390
+ this.socket = null;
391
+ try {
392
+ socket.close(1000, "client");
393
+ }
394
+ catch (error) {
395
+ this.log("debug", "socket.close threw", error);
396
+ }
397
+ if (this.boundClose)
398
+ socket.removeEventListener("close", this.boundClose);
399
+ this.boundClose = null;
400
+ this.handleClose({ code: 1000 });
401
+ }
402
+ log(level, message, detail) {
403
+ this.options.onLog?.(level, message, detail);
404
+ }
405
+ }
406
+ exports.MediaSignal = MediaSignal;
407
+ function toText(data) {
408
+ if (typeof data === "string")
409
+ return data;
410
+ if (data instanceof Uint8Array)
411
+ return new TextDecoder().decode(data);
412
+ if (data instanceof ArrayBuffer)
413
+ return new TextDecoder().decode(new Uint8Array(data));
414
+ return null;
415
+ }
416
+ //# sourceMappingURL=signal.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"signal.js","sourceRoot":"","sources":["../../src/core/signal.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,57 @@
1
+ /**
2
+ * The one WebSocket shape `core/` knows about.
3
+ *
4
+ * Four hosts have to drive this client and no two of them have the same socket:
5
+ * a browser has the DOM one, Node 24 has a global `WebSocket`, older Node
6
+ * services carry `ws`, and the load harness may want a socket that lies about
7
+ * latency. Rather than branch on the environment, `core/` takes a factory and
8
+ * never constructs anything itself.
9
+ *
10
+ * The listener style is `addEventListener`, because that is the intersection:
11
+ * the browser, Node's global and the `ws` package all provide it, while only
12
+ * `ws` provides `.on()`.
13
+ */
14
+ export declare const SOCKET_CONNECTING = 0;
15
+ export declare const SOCKET_OPEN = 1;
16
+ export declare const SOCKET_CLOSING = 2;
17
+ export declare const SOCKET_CLOSED = 3;
18
+ export type SocketOpenEvent = {
19
+ type: "open";
20
+ };
21
+ export type SocketMessageEvent = {
22
+ type: "message";
23
+ data: unknown;
24
+ };
25
+ export type SocketErrorEvent = {
26
+ type: "error";
27
+ error?: unknown;
28
+ };
29
+ export type SocketCloseEvent = {
30
+ type: "close";
31
+ code?: number;
32
+ reason?: string;
33
+ };
34
+ export type SocketEventMap = {
35
+ open: SocketOpenEvent;
36
+ message: SocketMessageEvent;
37
+ error: SocketErrorEvent;
38
+ close: SocketCloseEvent;
39
+ };
40
+ export interface WebSocketLike {
41
+ readonly readyState: number;
42
+ send(data: string): void;
43
+ close(code?: number, reason?: string): void;
44
+ addEventListener<K extends keyof SocketEventMap>(type: K, listener: (event: SocketEventMap[K]) => void): void;
45
+ removeEventListener<K extends keyof SocketEventMap>(type: K, listener: (event: SocketEventMap[K]) => void): void;
46
+ }
47
+ export type MediaWebSocketFactory = (url: string) => WebSocketLike;
48
+ /**
49
+ * Resolves `globalThis.WebSocket` at CALL time, never at module load.
50
+ *
51
+ * Reading it at module scope would make importing this package fail outright in
52
+ * a host that has no global socket but was going to pass its own factory, and
53
+ * "importing a type broke my service" is the class of problem the isomorphic
54
+ * split exists to prevent.
55
+ */
56
+ export declare function defaultWebSocketFactory(url: string): WebSocketLike;
57
+ //# sourceMappingURL=socket.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"socket.d.ts","sourceRoot":"","sources":["../../src/core/socket.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;GAYG;AAEH,eAAO,MAAM,iBAAiB,IAAI,CAAC;AACnC,eAAO,MAAM,WAAW,IAAI,CAAC;AAC7B,eAAO,MAAM,cAAc,IAAI,CAAC;AAChC,eAAO,MAAM,aAAa,IAAI,CAAC;AAE/B,MAAM,MAAM,eAAe,GAAG;IAAE,IAAI,EAAE,MAAM,CAAA;CAAE,CAAC;AAC/C,MAAM,MAAM,kBAAkB,GAAG;IAAE,IAAI,EAAE,SAAS,CAAC;IAAC,IAAI,EAAE,OAAO,CAAA;CAAE,CAAC;AACpE,MAAM,MAAM,gBAAgB,GAAG;IAAE,IAAI,EAAE,OAAO,CAAC;IAAC,KAAK,CAAC,EAAE,OAAO,CAAA;CAAE,CAAC;AAClE,MAAM,MAAM,gBAAgB,GAAG;IAAE,IAAI,EAAE,OAAO,CAAC;IAAC,IAAI,CAAC,EAAE,MAAM,CAAC;IAAC,MAAM,CAAC,EAAE,MAAM,CAAA;CAAE,CAAC;AAEjF,MAAM,MAAM,cAAc,GAAG;IAC3B,IAAI,EAAE,eAAe,CAAC;IACtB,OAAO,EAAE,kBAAkB,CAAC;IAC5B,KAAK,EAAE,gBAAgB,CAAC;IACxB,KAAK,EAAE,gBAAgB,CAAC;CACzB,CAAC;AAEF,MAAM,WAAW,aAAa;IAC5B,QAAQ,CAAC,UAAU,EAAE,MAAM,CAAC;IAC5B,IAAI,CAAC,IAAI,EAAE,MAAM,GAAG,IAAI,CAAC;IACzB,KAAK,CAAC,IAAI,CAAC,EAAE,MAAM,EAAE,MAAM,CAAC,EAAE,MAAM,GAAG,IAAI,CAAC;IAC5C,gBAAgB,CAAC,CAAC,SAAS,MAAM,cAAc,EAAE,IAAI,EAAE,CAAC,EAAE,QAAQ,EAAE,CAAC,KAAK,EAAE,cAAc,CAAC,CAAC,CAAC,KAAK,IAAI,GAAG,IAAI,CAAC;IAC9G,mBAAmB,CAAC,CAAC,SAAS,MAAM,cAAc,EAAE,IAAI,EAAE,CAAC,EAAE,QAAQ,EAAE,CAAC,KAAK,EAAE,cAAc,CAAC,CAAC,CAAC,KAAK,IAAI,GAAG,IAAI,CAAC;CAClH;AAED,MAAM,MAAM,qBAAqB,GAAG,CAAC,GAAG,EAAE,MAAM,KAAK,aAAa,CAAC;AAEnE;;;;;;;GAOG;AACH,wBAAgB,uBAAuB,CAAC,GAAG,EAAE,MAAM,GAAG,aAAa,CAQlE"}
@@ -0,0 +1,37 @@
1
+ "use strict";
2
+ /**
3
+ * The one WebSocket shape `core/` knows about.
4
+ *
5
+ * Four hosts have to drive this client and no two of them have the same socket:
6
+ * a browser has the DOM one, Node 24 has a global `WebSocket`, older Node
7
+ * services carry `ws`, and the load harness may want a socket that lies about
8
+ * latency. Rather than branch on the environment, `core/` takes a factory and
9
+ * never constructs anything itself.
10
+ *
11
+ * The listener style is `addEventListener`, because that is the intersection:
12
+ * the browser, Node's global and the `ws` package all provide it, while only
13
+ * `ws` provides `.on()`.
14
+ */
15
+ Object.defineProperty(exports, "__esModule", { value: true });
16
+ exports.SOCKET_CLOSED = exports.SOCKET_CLOSING = exports.SOCKET_OPEN = exports.SOCKET_CONNECTING = void 0;
17
+ exports.defaultWebSocketFactory = defaultWebSocketFactory;
18
+ exports.SOCKET_CONNECTING = 0;
19
+ exports.SOCKET_OPEN = 1;
20
+ exports.SOCKET_CLOSING = 2;
21
+ exports.SOCKET_CLOSED = 3;
22
+ /**
23
+ * Resolves `globalThis.WebSocket` at CALL time, never at module load.
24
+ *
25
+ * Reading it at module scope would make importing this package fail outright in
26
+ * a host that has no global socket but was going to pass its own factory, and
27
+ * "importing a type broke my service" is the class of problem the isomorphic
28
+ * split exists to prevent.
29
+ */
30
+ function defaultWebSocketFactory(url) {
31
+ const ctor = globalThis.WebSocket;
32
+ if (!ctor) {
33
+ throw new Error("no global WebSocket in this runtime. Pass `webSocket: (url) => new WS(url)` to the signal client.");
34
+ }
35
+ return new ctor(url);
36
+ }
37
+ //# sourceMappingURL=socket.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"socket.js","sourceRoot":"","sources":["../../src/core/socket.ts"],"names":[],"mappings":";AAAA;;;;;;;;;;;;GAYG;;;AAqCH,0DAQC;AA3CY,QAAA,iBAAiB,GAAG,CAAC,CAAC;AACtB,QAAA,WAAW,GAAG,CAAC,CAAC;AAChB,QAAA,cAAc,GAAG,CAAC,CAAC;AACnB,QAAA,aAAa,GAAG,CAAC,CAAC;AAwB/B;;;;;;;GAOG;AACH,SAAgB,uBAAuB,CAAC,GAAW;IACjD,MAAM,IAAI,GAAI,UAA2D,CAAC,SAAS,CAAC;IACpF,IAAI,CAAC,IAAI,EAAE,CAAC;QACV,MAAM,IAAI,KAAK,CACb,mGAAmG,CACpG,CAAC;IACJ,CAAC;IACD,OAAO,IAAI,IAAI,CAAC,GAAG,CAA6B,CAAC;AACnD,CAAC"}
@@ -0,0 +1,67 @@
1
+ import { type EventFrame, type Peer, type SubscribeRule } from "@tribe-nest/media-protocol";
2
+ /**
3
+ * The room as the event frames describe it, as a pure reducer.
4
+ *
5
+ * Pure because this is the part that is worth testing exhaustively and the part
6
+ * a socket makes untestable. Every ordering that matters (a peer leaving before
7
+ * its producers close, an active-speaker set naming a producer that has just
8
+ * gone, a rule narrowing mid-call) is a two-line test here and a flaky
9
+ * integration run anywhere else.
10
+ *
11
+ * It holds no tracks, no consumers and no mediasoup objects. Those belong to
12
+ * P3b's room API, keyed by the `producerId` this state carries.
13
+ */
14
+ export type ProducerEntry = {
15
+ producerId: string;
16
+ identity: string;
17
+ kind: "audio" | "video";
18
+ paused: boolean;
19
+ };
20
+ export type RoomState = {
21
+ phase: "idle" | "joined" | "closed";
22
+ /** Our own identity, as the node reported it. */
23
+ identity: string | null;
24
+ room: string | null;
25
+ /** In arrival order, which is the only order the wire gives. */
26
+ peers: readonly Peer[];
27
+ /**
28
+ * EVERY producer the node has announced, whether or not the current
29
+ * subscribe rule permits it. Visibility is derived by `visibleProducers`;
30
+ * see the note on `subscribeRuleChanged` for why this is not pruned.
31
+ */
32
+ producers: readonly ProducerEntry[];
33
+ /** The set the NODE decided we should consume, in its order. */
34
+ activeSpeakers: readonly string[];
35
+ /** True while any `kind: "egress"` participant is present. A consent flag. */
36
+ recording: boolean;
37
+ /** The latest rule pushed for us, if one ever was. */
38
+ subscribeRule: SubscribeRule | null;
39
+ /** Set by `draining`, cleared by the next `joined`. */
40
+ draining: {
41
+ reconnectAfterMs: number;
42
+ } | null;
43
+ closedReason: string | null;
44
+ };
45
+ export declare const initialRoomState: RoomState;
46
+ /**
47
+ * Apply one event frame.
48
+ *
49
+ * Never throws and never mutates. An event it does not recognise returns the
50
+ * SAME reference, so a caller can use identity to skip a re-render.
51
+ */
52
+ export declare function reduceRoomState(state: RoomState, frame: EventFrame): RoomState;
53
+ /** Convenience for a caller feeding a stream of frames, e.g. `signal.onAny`. */
54
+ export declare function reduceRoomStateAll(state: RoomState, frames: readonly EventFrame[]): RoomState;
55
+ export declare function producerById(state: RoomState, producerId: string): ProducerEntry | undefined;
56
+ export declare function producersOf(state: RoomState, identity: string): readonly ProducerEntry[];
57
+ /** The active set intersected with what we actually know about, in the node's order. */
58
+ export declare function activeProducers(state: RoomState): readonly ProducerEntry[];
59
+ /**
60
+ * What this participant may actually receive.
61
+ *
62
+ * Derived rather than stored, so a barrier that narrows and later widens is
63
+ * reversible. The reducer keeps every producer it has been told about; this is
64
+ * what a UI should render and what a client should consume from.
65
+ */
66
+ export declare function visibleProducers(state: RoomState): readonly ProducerEntry[];
67
+ //# sourceMappingURL=state.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"state.d.ts","sourceRoot":"","sources":["../../src/core/state.ts"],"names":[],"mappings":"AAAA,OAAO,EAAgB,KAAK,UAAU,EAAoB,KAAK,IAAI,EAAE,KAAK,aAAa,EAAE,MAAM,4BAA4B,CAAC;AAE5H;;;;;;;;;;;GAWG;AAEH,MAAM,MAAM,aAAa,GAAG;IAC1B,UAAU,EAAE,MAAM,CAAC;IACnB,QAAQ,EAAE,MAAM,CAAC;IACjB,IAAI,EAAE,OAAO,GAAG,OAAO,CAAC;IACxB,MAAM,EAAE,OAAO,CAAC;CACjB,CAAC;AAEF,MAAM,MAAM,SAAS,GAAG;IACtB,KAAK,EAAE,MAAM,GAAG,QAAQ,GAAG,QAAQ,CAAC;IACpC,iDAAiD;IACjD,QAAQ,EAAE,MAAM,GAAG,IAAI,CAAC;IACxB,IAAI,EAAE,MAAM,GAAG,IAAI,CAAC;IACpB,gEAAgE;IAChE,KAAK,EAAE,SAAS,IAAI,EAAE,CAAC;IACvB;;;;OAIG;IACH,SAAS,EAAE,SAAS,aAAa,EAAE,CAAC;IACpC,gEAAgE;IAChE,cAAc,EAAE,SAAS,MAAM,EAAE,CAAC;IAClC,8EAA8E;IAC9E,SAAS,EAAE,OAAO,CAAC;IACnB,sDAAsD;IACtD,aAAa,EAAE,aAAa,GAAG,IAAI,CAAC;IACpC,uDAAuD;IACvD,QAAQ,EAAE;QAAE,gBAAgB,EAAE,MAAM,CAAA;KAAE,GAAG,IAAI,CAAC;IAC9C,YAAY,EAAE,MAAM,GAAG,IAAI,CAAC;CAC7B,CAAC;AAEF,eAAO,MAAM,gBAAgB,EAAE,SAW9B,CAAC;AAEF;;;;;GAKG;AACH,wBAAgB,eAAe,CAAC,KAAK,EAAE,SAAS,EAAE,KAAK,EAAE,UAAU,GAAG,SAAS,CAkH9E;AAED,gFAAgF;AAChF,wBAAgB,kBAAkB,CAAC,KAAK,EAAE,SAAS,EAAE,MAAM,EAAE,SAAS,UAAU,EAAE,GAAG,SAAS,CAE7F;AAED,wBAAgB,YAAY,CAAC,KAAK,EAAE,SAAS,EAAE,UAAU,EAAE,MAAM,GAAG,aAAa,GAAG,SAAS,CAE5F;AAED,wBAAgB,WAAW,CAAC,KAAK,EAAE,SAAS,EAAE,QAAQ,EAAE,MAAM,GAAG,SAAS,aAAa,EAAE,CAExF;AAED,wFAAwF;AACxF,wBAAgB,eAAe,CAAC,KAAK,EAAE,SAAS,GAAG,SAAS,aAAa,EAAE,CAO1E;AAYD;;;;;;GAMG;AACH,wBAAgB,gBAAgB,CAAC,KAAK,EAAE,SAAS,GAAG,SAAS,aAAa,EAAE,CAE3E"}