@rapidmx/meet-plugin 0.9.3 → 0.11.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 (52) hide show
  1. package/apps/meet/[token].tsx +204 -13
  2. package/apps/meet/_CallControls.tsx +75 -5
  3. package/apps/meet/_CallView.tsx +326 -7
  4. package/apps/meet/_DiagnosticsPanel.tsx +113 -0
  5. package/apps/meet/_ParticipantTile.tsx +10 -2
  6. package/apps/meet/_ParticipantsDrawer.tsx +397 -0
  7. package/apps/meet/_icons.tsx +12 -0
  8. package/apps/meet/_meetApi.ts +141 -1
  9. package/apps/shared/media/filters/ScreenTransform.ts +148 -0
  10. package/apps/shared/media/useLocalMedia.ts +7 -3
  11. package/apps/shared/webrtc/MeshConnectionManager.ts +115 -4
  12. package/apps/shared/webrtc/realPeerConnection.ts +47 -7
  13. package/apps/shared/webrtc/types.ts +67 -8
  14. package/dist/apps/meet/[token].d.ts +4 -1
  15. package/dist/apps/meet/[token].js +121 -9
  16. package/dist/apps/meet/_CallControls.d.ts +16 -2
  17. package/dist/apps/meet/_CallControls.js +9 -6
  18. package/dist/apps/meet/_CallView.d.ts +45 -2
  19. package/dist/apps/meet/_CallView.js +215 -6
  20. package/dist/apps/meet/_DiagnosticsPanel.d.ts +22 -0
  21. package/dist/apps/meet/_DiagnosticsPanel.js +32 -0
  22. package/dist/apps/meet/_ParticipantTile.d.ts +8 -0
  23. package/dist/apps/meet/_ParticipantTile.js +9 -2
  24. package/dist/apps/meet/_ParticipantsDrawer.d.ts +77 -0
  25. package/dist/apps/meet/_ParticipantsDrawer.js +91 -0
  26. package/dist/apps/meet/_icons.d.ts +3 -0
  27. package/dist/apps/meet/_icons.js +3 -0
  28. package/dist/apps/meet/_meetApi.d.ts +93 -1
  29. package/dist/apps/meet/_meetApi.js +82 -0
  30. package/dist/apps/shared/media/filters/ScreenTransform.d.ts +53 -0
  31. package/dist/apps/shared/media/filters/ScreenTransform.js +100 -0
  32. package/dist/apps/shared/media/useLocalMedia.d.ts +5 -1
  33. package/dist/apps/shared/media/useLocalMedia.js +3 -3
  34. package/dist/apps/shared/webrtc/MeshConnectionManager.d.ts +49 -1
  35. package/dist/apps/shared/webrtc/MeshConnectionManager.js +103 -3
  36. package/dist/apps/shared/webrtc/realPeerConnection.d.ts +19 -6
  37. package/dist/apps/shared/webrtc/realPeerConnection.js +42 -5
  38. package/dist/apps/shared/webrtc/types.d.ts +69 -7
  39. package/dist/lib/models/mongo/VideoMeetingMongo.js +21 -0
  40. package/dist/lib/models/mongo/VideoMeetingMongo.js.map +1 -1
  41. package/dist/lib/models/sql/VideoMeetingSQL.js +21 -0
  42. package/dist/lib/models/sql/VideoMeetingSQL.js.map +1 -1
  43. package/dist/lib/routes/BaseVideoMeetingRoute.js +380 -51
  44. package/dist/lib/routes/BaseVideoMeetingRoute.js.map +1 -1
  45. package/dist/lib/util/PasswordUtils.js +52 -0
  46. package/dist/lib/util/PasswordUtils.js.map +1 -0
  47. package/dist/types/models/mongo/VideoMeetingMongo.d.ts +3 -0
  48. package/dist/types/models/sql/VideoMeetingSQL.d.ts +3 -0
  49. package/dist/types/models/types.d.ts +23 -0
  50. package/dist/types/routes/BaseVideoMeetingRoute.d.ts +147 -5
  51. package/dist/types/util/PasswordUtils.d.ts +17 -0
  52. package/package.json +1 -1
@@ -62,6 +62,28 @@
62
62
  *
63
63
  * Sharing a screen is `setLocalTrack("video", screenTrack)`; stopping is `setLocalTrack("video", cameraTrack)`.
64
64
  *
65
+ * ## Host moderation (`mute-request`, `kicked`)
66
+ *
67
+ * `sendMuteRequest()`/`sendKick()` send a point-to-point signal to one peer; the manager itself does not decide who
68
+ * may send one - a "host" identity exists only in `_CallView.tsx`, which shows the controls that call these
69
+ * methods to nobody but the host (see `types.ts`'s `SignalMessage` doc comment for why this is cooperative, not
70
+ * enforced, at the signaling layer). Receiving one emits `mute-requested`/`kicked` for the caller to act on; this
71
+ * manager takes no action of its own beyond that - it doesn't mute a track or tear itself down, since a kicked
72
+ * participant's actual departure is `stop()`, which the caller decides to call.
73
+ *
74
+ * ## Talking stick (`talking-stick`)
75
+ *
76
+ * One exclusive "floor" - at most one participant may be unmuted at a time, host-assigned - but unlike presenter,
77
+ * there is no claim race to resolve: only the host's own client ever calls `setTalkingStick()` (same trust level as
78
+ * `sendMuteRequest()`/`sendKick()` - nothing here checks that), so the manager carries no state of its own and
79
+ * simply relays the latest message it sees as `talking-stick-changed`, applying it to itself too via an immediate
80
+ * local emit (the same "optimistic, no round trip" shape `claimPresenter()`/`releasePresenter()` use). A holder who
81
+ * leaves the call is not specially reassigned - every other participant's existing `participant-left` handling
82
+ * already drops them from the roster, and "the stick's holder is no longer in the call" is simply "nobody is holder
83
+ * right now" without any manager needing to say so, until the host picks someone (or themselves) again. There is no
84
+ * backend route or persisted field for this at all: like `mute-request`/`kicked`, it only matters while people are
85
+ * actually in the call together.
86
+ *
65
87
  * ## Three media paths, tried in order
66
88
  *
67
89
  * Each pair of participants reaches for the best path that works, and `MeshParticipant.transport` says which one it
@@ -98,6 +120,10 @@ const MAX_ORPHAN_CANDIDATES = 64;
98
120
  export const DEFAULT_CONNECT_TIMEOUT_MS = 15000;
99
121
  /** How long a connection may stay `disconnected` (ICE lost the path but may still recover) before giving up on it. */
100
122
  export const DEFAULT_DISCONNECTED_GRACE_MS = 8000;
123
+ /** How often a connected pair's quality stats (the diagnostics panel) are refreshed - low frequency on purpose:
124
+ * this is for a participant occasionally checking why a call feels off, not a live chart, and `getStats()` is not
125
+ * free to call every frame. */
126
+ export const DEFAULT_DIAGNOSTICS_POLL_MS = 3000;
101
127
  export class MeshConnectionManager {
102
128
  constructor(options) {
103
129
  this.options = options;
@@ -152,6 +178,7 @@ export class MeshConnectionManager {
152
178
  this.unsubscribe = undefined;
153
179
  for (const peer of this.peers.values()) {
154
180
  this.clearTimers(peer);
181
+ this.stopDiagnosticsPolling(peer);
155
182
  peer.pc.close();
156
183
  }
157
184
  this.peers.clear();
@@ -209,6 +236,27 @@ export class MeshConnectionManager {
209
236
  this.send({ kind: "presenter-release" });
210
237
  this.emit({ type: "presenter-changed", uid: undefined });
211
238
  }
239
+ /** Asks `peerUid`'s participant to mute - a cooperative signal (see this module's doc comment on host
240
+ * moderation), not enforcement. Nothing here checks that the local participant is actually the host; the caller
241
+ * is responsible for only offering this to one. */
242
+ sendMuteRequest(peerUid) {
243
+ this.send({ kind: "mute-request", to: peerUid });
244
+ }
245
+ /** Tells `peerUid`'s participant they have been removed from the call. Sends only the cooperative signal - it
246
+ * does not itself revoke `peerUid`'s server-side channel grant (see `BaseVideoMeetingRoute.revokeChannelGrant()`,
247
+ * which the caller is expected to call alongside this for an enforced removal). */
248
+ sendKick(peerUid) {
249
+ this.send({ kind: "kicked", to: peerUid });
250
+ }
251
+ /** Turns talking-stick mode on (naming `holder`, the initial speaker) or off (`active: false`, `holder`
252
+ * omitted), or hands an already-active stick to a different `holder` - see this module's doc comment on why
253
+ * there is no collision to resolve here, unlike presenter. Broadcasts and applies locally at once, trusting
254
+ * there is only ever one sender; the caller (`_CallView.tsx`) is responsible for only offering this to the
255
+ * host. */
256
+ setTalkingStick(active, holder) {
257
+ this.send({ kind: "talking-stick", active, holder });
258
+ this.emit({ type: "talking-stick-changed", active, holder });
259
+ }
212
260
  sendHello() {
213
261
  this.send({ kind: "hello", name: this.options.selfName, state: this.localState });
214
262
  }
@@ -266,6 +314,15 @@ export class MeshConnectionManager {
266
314
  case "relay-fallback":
267
315
  this.handleRelayFallback(message.from);
268
316
  return;
317
+ case "mute-request":
318
+ this.emit({ type: "mute-requested" });
319
+ return;
320
+ case "kicked":
321
+ this.emit({ type: "kicked" });
322
+ return;
323
+ case "talking-stick":
324
+ this.emit({ type: "talking-stick-changed", active: message.active === true, holder: typeof message.holder === "string" ? message.holder : undefined });
325
+ return;
269
326
  }
270
327
  }
271
328
  handleHello(message) {
@@ -324,6 +381,7 @@ export class MeshConnectionManager {
324
381
  return;
325
382
  }
326
383
  this.clearTimers(peer);
384
+ this.stopDiagnosticsPolling(peer);
327
385
  peer.pc.close();
328
386
  this.peers.delete(uid);
329
387
  if (peer.transport === "websocket") {
@@ -355,6 +413,7 @@ export class MeshConnectionManager {
355
413
  transport: "connecting",
356
414
  connectTimer: undefined,
357
415
  disconnectTimer: undefined,
416
+ diagnosticsTimer: undefined,
358
417
  };
359
418
  this.orphanCandidates.delete(uid);
360
419
  this.peers.set(uid, peer);
@@ -397,7 +456,8 @@ export class MeshConnectionManager {
397
456
  return;
398
457
  }
399
458
  }
400
- /** Records whether a connected pair is direct or relayed through TURN, from the candidate pair ICE selected. */
459
+ /** Records whether a connected pair is direct, relayed through TURN, or relayed through TURN over TCP (see
460
+ * `MediaTransport`'s doc comment), from the candidate pair ICE selected. */
401
461
  async detectTransport(peer) {
402
462
  let type;
403
463
  try {
@@ -408,7 +468,37 @@ export class MeshConnectionManager {
408
468
  }
409
469
  // The pair may have moved on (fallen back, left, or dropped again) while the stats were being read.
410
470
  if (peer.transport !== "websocket" && this.peers.get(peer.uid) === peer && peer.pc.connectionState === "connected") {
411
- this.setTransport(peer, type === "turn" ? "turn" : "p2p");
471
+ this.setTransport(peer, type === "turn" || type === "turn-tcp" ? type : "p2p");
472
+ this.startDiagnosticsPolling(peer);
473
+ }
474
+ }
475
+ /** Idempotent - a pair that reconnects without ever leaving WebRTC (`connected` -> `disconnected` -> `connected`
476
+ * again) does not get a second timer. */
477
+ startDiagnosticsPolling(peer) {
478
+ if (peer.diagnosticsTimer !== undefined) {
479
+ return;
480
+ }
481
+ const pollMs = this.options.diagnosticsPollMs ?? DEFAULT_DIAGNOSTICS_POLL_MS;
482
+ peer.diagnosticsTimer = setInterval(() => void this.pollDiagnostics(peer), pollMs);
483
+ void this.pollDiagnostics(peer); // the first sample doesn't wait a full interval
484
+ }
485
+ stopDiagnosticsPolling(peer) {
486
+ clearInterval(peer.diagnosticsTimer);
487
+ peer.diagnosticsTimer = undefined;
488
+ }
489
+ async pollDiagnostics(peer) {
490
+ let diagnostics;
491
+ try {
492
+ diagnostics = await peer.pc.collectDiagnostics();
493
+ }
494
+ catch {
495
+ // Leaves whatever was last polled in place rather than blanking a momentary failure.
496
+ return;
497
+ }
498
+ // The pair may have moved on (fallen back, left, or dropped again) while the stats were being read.
499
+ if (this.peers.get(peer.uid) === peer && peer.diagnosticsTimer !== undefined) {
500
+ peer.diagnostics = diagnostics;
501
+ this.emit({ type: "participant-updated", participant: toParticipant(peer) });
412
502
  }
413
503
  }
414
504
  setTransport(peer, transport) {
@@ -433,6 +523,8 @@ export class MeshConnectionManager {
433
523
  return;
434
524
  }
435
525
  this.clearTimers(peer);
526
+ this.stopDiagnosticsPolling(peer);
527
+ peer.diagnostics = undefined;
436
528
  const relay = this.options.relay;
437
529
  if (!relay?.supported) {
438
530
  this.setTransport(peer, "failed");
@@ -569,7 +661,15 @@ export class MeshConnectionManager {
569
661
  }
570
662
  }
571
663
  function toParticipant(peer) {
572
- return { uid: peer.uid, name: peer.name, audioOn: peer.audioOn, videoOn: peer.videoOn, handRaised: peer.handRaised, transport: peer.transport };
664
+ return {
665
+ uid: peer.uid,
666
+ name: peer.name,
667
+ audioOn: peer.audioOn,
668
+ videoOn: peer.videoOn,
669
+ handRaised: peer.handRaised,
670
+ transport: peer.transport,
671
+ diagnostics: peer.diagnostics,
672
+ };
573
673
  }
574
674
  function sameState(a, b) {
575
675
  return a.audioOn === b.audioOn && a.videoOn === b.videoOn && a.handRaised === b.handRaised;
@@ -3,12 +3,25 @@
3
3
  * this factory's only real caller. `createBrowserPeerConnection` is a plain function value, never invoked at
4
4
  * module scope, so importing this file is safe during this plugin's page SSR, where `RTCPeerConnection` does not
5
5
  * exist at all - it would only throw if actually *called* outside a browser. */
6
- import type { RTCPeerConnectionFactory } from "./types.js";
6
+ import type { ConnectionDiagnostics, RTCPeerConnectionFactory } from "./types.js";
7
7
  /**
8
- * Whether the pair ICE settled on goes through a TURN relay. The selected pair is found the standard way (the
9
- * `transport` report's `selectedCandidatePairId`) and, for a browser that does not fill that in, as the succeeded and
10
- * nominated `candidate-pair`. A relay on *either* end means the media passes through a TURN server, so it counts as
11
- * `"turn"`; `"unknown"` when no pair is reported at all.
8
+ * Whether the pair ICE settled on goes through a TURN relay, and - when it's our own end that's relayed - whether
9
+ * that relay is TCP (`turn-tcp`, meaning `turns:` too, TLS being TCP-based as well) rather than UDP. The selected
10
+ * pair is found the standard way (the `transport` report's `selectedCandidatePairId`) and, for a browser that does
11
+ * not fill that in, as the succeeded and nominated `candidate-pair`. A relay on *either* end means the media
12
+ * passes through a TURN server, so it counts as at least `"turn"`; `"unknown"` when no pair is reported at all.
13
+ *
14
+ * The TCP/UDP distinction matters because they fail very differently under loss: UDP just drops the lost packet
15
+ * (a click), while TCP retransmits it and blocks everything queued behind it until that arrives (a stall) - exactly
16
+ * the "gaps that feel like latency, not packet loss" a participant on a TCP-relayed connection reports. `relayProtocol`
17
+ * (`"udp"`/`"tcp"`/`"tls"`) is only ever reported on a *local* relay candidate - there's no equivalent visibility into
18
+ * how the *remote* peer reaches its own TURN allocation, so a relay on the remote end alone is reported as plain
19
+ * `"turn"` rather than guessed at.
12
20
  */
13
- export declare function selectedConnectionType(pc: Pick<RTCPeerConnection, "getStats">): Promise<"p2p" | "turn" | "unknown">;
21
+ export declare function selectedConnectionType(pc: Pick<RTCPeerConnection, "getStats">): Promise<"p2p" | "turn" | "turn-tcp" | "unknown">;
22
+ /** One point-in-time sample of this connection's own quality stats, for the diagnostics panel - a single
23
+ * `getStats()` walk, independent of (and in addition to) `selectedConnectionType()`'s own call, since this is
24
+ * polled periodically at a much lower priority than the one-time transport check and there's no value in coupling
25
+ * the two together. */
26
+ export declare function collectDiagnostics(pc: Pick<RTCPeerConnection, "getStats">): Promise<ConnectionDiagnostics>;
14
27
  export declare const createBrowserPeerConnection: RTCPeerConnectionFactory;
@@ -1,8 +1,16 @@
1
1
  /**
2
- * Whether the pair ICE settled on goes through a TURN relay. The selected pair is found the standard way (the
3
- * `transport` report's `selectedCandidatePairId`) and, for a browser that does not fill that in, as the succeeded and
4
- * nominated `candidate-pair`. A relay on *either* end means the media passes through a TURN server, so it counts as
5
- * `"turn"`; `"unknown"` when no pair is reported at all.
2
+ * Whether the pair ICE settled on goes through a TURN relay, and - when it's our own end that's relayed - whether
3
+ * that relay is TCP (`turn-tcp`, meaning `turns:` too, TLS being TCP-based as well) rather than UDP. The selected
4
+ * pair is found the standard way (the `transport` report's `selectedCandidatePairId`) and, for a browser that does
5
+ * not fill that in, as the succeeded and nominated `candidate-pair`. A relay on *either* end means the media
6
+ * passes through a TURN server, so it counts as at least `"turn"`; `"unknown"` when no pair is reported at all.
7
+ *
8
+ * The TCP/UDP distinction matters because they fail very differently under loss: UDP just drops the lost packet
9
+ * (a click), while TCP retransmits it and blocks everything queued behind it until that arrives (a stall) - exactly
10
+ * the "gaps that feel like latency, not packet loss" a participant on a TCP-relayed connection reports. `relayProtocol`
11
+ * (`"udp"`/`"tcp"`/`"tls"`) is only ever reported on a *local* relay candidate - there's no equivalent visibility into
12
+ * how the *remote* peer reaches its own TURN allocation, so a relay on the remote end alone is reported as plain
13
+ * `"turn"` rather than guessed at.
6
14
  */
7
15
  export async function selectedConnectionType(pc) {
8
16
  const reports = new Map();
@@ -15,7 +23,35 @@ export async function selectedConnectionType(pc) {
15
23
  }
16
24
  const local = reports.get(String(pair.localCandidateId));
17
25
  const remote = reports.get(String(pair.remoteCandidateId));
18
- return local?.candidateType === "relay" || remote?.candidateType === "relay" ? "turn" : "p2p";
26
+ if (local?.candidateType !== "relay" && remote?.candidateType !== "relay") {
27
+ return "p2p";
28
+ }
29
+ const relayProtocol = local?.candidateType === "relay" ? String(local.relayProtocol ?? "") : "";
30
+ return relayProtocol === "tcp" || relayProtocol === "tls" ? "turn-tcp" : "turn";
31
+ }
32
+ function streamDiagnostics(reports, kind) {
33
+ const inbound = [...reports.values()].find((report) => report.type === "inbound-rtp" && report.kind === kind);
34
+ const outbound = [...reports.values()].find((report) => report.type === "outbound-rtp" && report.kind === kind);
35
+ const num = (value) => (typeof value === "number" ? value : undefined);
36
+ return {
37
+ packetsLost: num(inbound?.packetsLost),
38
+ jitter: num(inbound?.jitter),
39
+ bytesSent: num(outbound?.bytesSent),
40
+ bytesReceived: num(inbound?.bytesReceived),
41
+ };
42
+ }
43
+ /** One point-in-time sample of this connection's own quality stats, for the diagnostics panel - a single
44
+ * `getStats()` walk, independent of (and in addition to) `selectedConnectionType()`'s own call, since this is
45
+ * polled periodically at a much lower priority than the one-time transport check and there's no value in coupling
46
+ * the two together. */
47
+ export async function collectDiagnostics(pc) {
48
+ const reports = new Map();
49
+ (await pc.getStats()).forEach((report) => reports.set(String(report.id), report));
50
+ const transport = [...reports.values()].find((report) => report.type === "transport" && report.selectedCandidatePairId);
51
+ const pair = (transport ? reports.get(String(transport.selectedCandidatePairId)) : undefined) ??
52
+ [...reports.values()].find((report) => report.type === "candidate-pair" && (report.selected || (report.nominated && report.state === "succeeded")));
53
+ const roundTripTimeSeconds = typeof pair?.currentRoundTripTime === "number" ? pair.currentRoundTripTime : undefined;
54
+ return { roundTripTimeSeconds, audio: streamDiagnostics(reports, "audio"), video: streamDiagnostics(reports, "video") };
19
55
  }
20
56
  export const createBrowserPeerConnection = (config) => {
21
57
  const pc = new RTCPeerConnection(config);
@@ -42,6 +78,7 @@ export const createBrowserPeerConnection = (config) => {
42
78
  addIceCandidate: (candidate) => pc.addIceCandidate(candidate),
43
79
  close: () => pc.close(),
44
80
  connectionType: () => selectedConnectionType(pc),
81
+ collectDiagnostics: () => collectDiagnostics(pc),
45
82
  onicecandidate: null,
46
83
  ontrack: null,
47
84
  onconnectionstatechange: null,
@@ -21,14 +21,22 @@ export declare const REACTION_EMOJIS: readonly ["👍", "👏", "❤️", "🎉"
21
21
  * literal so a listener on the meeting's channel (which will also see other frame shapes if this server ever
22
22
  * grows other push consumers) can cheaply recognize and skip anything that isn't one of these.
23
23
  *
24
- * `hello`/`bye`/`presenter-claim`/`presenter-release`/`state`/`reaction` are broadcast (`to` left unset - every
25
- * participant is relevant); `offer`/`answer`/`ice-candidate` are point-to-point (`to` is the intended peer's uid) -
26
- * a recipient that isn't `to` (when set) ignores the message, since a channel's pub/sub fans every message out to
27
- * every subscriber, sender included.
24
+ * `hello`/`bye`/`presenter-claim`/`presenter-release`/`state`/`reaction`/`talking-stick` are broadcast (`to` left
25
+ * unset - every participant is relevant); `offer`/`answer`/`ice-candidate`/`mute-request`/`kicked` are
26
+ * point-to-point (`to` is the intended peer's uid) - a recipient that isn't `to` (when set) ignores the message,
27
+ * since a channel's pub/sub fans every message out to every subscriber, sender included.
28
+ *
29
+ * `mute-request`/`kicked`/`talking-stick` are host moderation signals (see `MeshConnectionManager`'s doc comment) -
30
+ * purely cooperative, like every other message here: the recipient's own client decides whether to honor one, and
31
+ * nothing on the wire distinguishes a genuine host's message from any other participant's, since this client
32
+ * protocol has no host identity of its own to check against server-side. `kicked` is paired with the sender also
33
+ * revoking the recipient's server-side channel grant (`BaseVideoMeetingRoute.revokeChannelGrant()`), which is
34
+ * enforced and does not depend on the recipient's client cooperating; `talking-stick` has no server-side
35
+ * counterpart at all (see this module's doc comment on why it is purely an in-call runtime signal).
28
36
  */
29
37
  export interface SignalMessage {
30
38
  type: "video-meeting-signal";
31
- kind: "hello" | "bye" | "offer" | "answer" | "ice-candidate" | "presenter-claim" | "presenter-release" | "state" | "reaction" | "relay-fallback";
39
+ kind: "hello" | "bye" | "offer" | "answer" | "ice-candidate" | "presenter-claim" | "presenter-release" | "state" | "reaction" | "relay-fallback" | "mute-request" | "kicked" | "talking-stick";
32
40
  /** The sender's authenticated uid - the real caller's own uid when `join()` returned `authenticated: true`, else
33
41
  * the `guest:<random>` uid `BaseVideoMeetingRoute.join()` minted. The server refuses a published message whose
34
42
  * `from` isn't the authenticated caller's uid (it stops one participant speaking as another), so this must be
@@ -47,6 +55,11 @@ export interface SignalMessage {
47
55
  state?: ParticipantState;
48
56
  /** `reaction` only - one of `REACTION_EMOJIS`. */
49
57
  emoji?: string;
58
+ /** `talking-stick` only - whether talking-stick mode is currently on. */
59
+ active?: boolean;
60
+ /** `talking-stick` only - the peer id (`MeshParticipant.uid`/this tab's own id) currently holding the stick.
61
+ * Absent when `active` is `false`, or when nobody holds it yet. */
62
+ holder?: string;
50
63
  /** `offer`/`answer` only. */
51
64
  sdp?: RTCSessionDescriptionInit;
52
65
  /** `ice-candidate` only. */
@@ -62,6 +75,26 @@ export interface SignalingChannel {
62
75
  * rather than relying on the transport to). Returns the function that stops listening. */
63
76
  onMessage(handler: (message: SignalMessage) => void): () => void;
64
77
  }
78
+ /** One kind's RTP stream stats, as of one sample - cumulative counters (`bytesSent`/`bytesReceived`), not a rate,
79
+ * since a single `getStats()` snapshot has no "since when" to divide by; a caller wanting a bitrate keeps the
80
+ * previous sample itself and divides the difference by the elapsed time (see `MeshConnectionManager`'s poller).
81
+ * `undefined` for anything this browser (or this stream's direction - an audio-only connection reports nothing for
82
+ * video, and vice versa) doesn't report. */
83
+ export interface RtpStreamDiagnostics {
84
+ packetsLost?: number;
85
+ /** Seconds. */
86
+ jitter?: number;
87
+ bytesSent?: number;
88
+ bytesReceived?: number;
89
+ }
90
+ /** One point-in-time sample of a connection's own quality stats, for the diagnostics panel. */
91
+ export interface ConnectionDiagnostics {
92
+ /** Seconds - the selected candidate pair's own current round-trip time. `undefined` when this browser doesn't
93
+ * report one (a connection still connecting, or simply not supported). */
94
+ roundTripTimeSeconds?: number;
95
+ audio: RtpStreamDiagnostics;
96
+ video: RtpStreamDiagnostics;
97
+ }
65
98
  /** The subset of `RTCRtpSender` `MeshConnectionManager.setLocalTrack()` uses. Every connection carries one audio and
66
99
  * one video sender for its whole life (see `MeshConnectionManager`'s doc comment), so turning a camera on or off,
67
100
  * switching a device or sharing a screen only ever replaces the track a sender sends - no renegotiation. A real
@@ -101,16 +134,23 @@ export interface RTCPeerConnectionLike {
101
134
  connectionState: string;
102
135
  /** Which path the connected pair is using - read from the selected candidate pair once `connectionState` is
103
136
  * `connected`. `"unknown"` when the browser does not report one (treated as direct by the caller). */
104
- connectionType(): Promise<"p2p" | "turn" | "unknown">;
137
+ connectionType(): Promise<"p2p" | "turn" | "turn-tcp" | "unknown">;
138
+ /** One point-in-time sample of this connection's own quality stats, for the diagnostics panel - polled
139
+ * periodically and independently of `connectionType()` (see `MeshConnectionManager`'s doc comment). */
140
+ collectDiagnostics(): Promise<ConnectionDiagnostics>;
105
141
  }
106
142
  /** Which of the three media paths a participant's audio and video currently take, in the order they are tried:
107
143
  *
108
144
  * - `"p2p"`: a direct peer-to-peer WebRTC connection, the preferred path.
109
145
  * - `"turn"`: still WebRTC, but relayed through the TURN server, for a participant a direct path cannot reach.
146
+ * - `"turn-tcp"`: the same, but the relay hop itself is TCP (or `turns:`, TLS being TCP-based too) rather than UDP -
147
+ * a network that blocks UDP outright. TCP's reliable, ordered delivery means one lost or delayed packet stalls
148
+ * everything queued behind it instead of just being dropped, so this can sound like pauses rather than the clicks
149
+ * plain packet loss would - worth telling apart from ordinary `"turn"` for exactly that reason.
110
150
  * - `"websocket"`: not WebRTC at all - encoded frames proxied by this server over a WebSocket (see
111
151
  * `apps/shared/relay/`), the last resort when neither of the above connects.
112
152
  * - `"connecting"`: no path established yet. `"failed"`: every path this browser can try has failed. */
113
- export type MediaTransport = "connecting" | "p2p" | "turn" | "websocket" | "failed";
153
+ export type MediaTransport = "connecting" | "p2p" | "turn" | "turn-tcp" | "websocket" | "failed";
114
154
  /** What `MeshConnectionManager` needs from the WebSocket media relay (`apps/shared/relay/RelayTransport.ts` implements
115
155
  * it) - declared here so the mesh, and its tests, depend on the shape alone. */
116
156
  export interface RelayTransportLike {
@@ -135,6 +175,10 @@ export interface MeshParticipant extends ParticipantState {
135
175
  name: string;
136
176
  /** How this participant's media currently reaches the local one - see `MediaTransport`. */
137
177
  transport: MediaTransport;
178
+ /** This connection's most recently polled quality stats, for the diagnostics panel - `undefined` until the
179
+ * first poll completes (shortly after `transport` first leaves `"connecting"`), and never present at all for
180
+ * a `"websocket"`-relayed participant (there is no `RTCPeerConnection` to poll). */
181
+ diagnostics?: ConnectionDiagnostics;
138
182
  }
139
183
  export type MeshEvent = {
140
184
  type: "participant-joined";
@@ -161,4 +205,22 @@ export type MeshEvent = {
161
205
  } | {
162
206
  type: "presenter-changed";
163
207
  uid: string | undefined;
208
+ }
209
+ /** Someone (implicitly the host, on the client's own say-so - see `SignalMessage`'s doc comment) asked the local
210
+ * participant to mute. The caller (`_CallView.tsx`) mutes only if currently unmuted; this never unmutes anyone. */
211
+ | {
212
+ type: "mute-requested";
213
+ }
214
+ /** The local participant was removed from the call. The caller tears down the mesh and shows a distinct
215
+ * "removed by the host" state rather than the ordinary "you left" one. */
216
+ | {
217
+ type: "kicked";
218
+ }
219
+ /** Talking-stick mode turned on or off, or the stick changed hands - see this module's doc comment and
220
+ * `MeshConnectionManager.setTalkingStick()`. `holder` is the peer id now holding it; absent while `active` is
221
+ * `false`, or (transiently) if whoever held it has since left the call. */
222
+ | {
223
+ type: "talking-stick-changed";
224
+ active: boolean;
225
+ holder?: string;
164
226
  };
@@ -43,6 +43,9 @@ let VideoMeetingMongo = class VideoMeetingMongo extends BaseMongoEntity {
43
43
  this.publicSlug = "publicSlug" in other ? other.publicSlug : this.publicSlug;
44
44
  this.organizerSlug = "organizerSlug" in other ? other.organizerSlug : this.organizerSlug;
45
45
  this.status = other.status !== undefined ? other.status : this.status;
46
+ this.forceMuteOnJoin = "forceMuteOnJoin" in other ? other.forceMuteOnJoin : this.forceMuteOnJoin;
47
+ this.passwordHash = "passwordHash" in other ? other.passwordHash : this.passwordHash;
48
+ this.waitingRoomEnabled = "waitingRoomEnabled" in other ? other.waitingRoomEnabled : this.waitingRoomEnabled;
46
49
  this.startTime = "startTime" in other ? other.startTime : this.startTime;
47
50
  this.endTime = "endTime" in other ? other.endTime : this.endTime;
48
51
  }
@@ -86,6 +89,24 @@ __decorate([
86
89
  Description("The current lifecycle state of this meeting."),
87
90
  __metadata("design:type", String)
88
91
  ], VideoMeetingMongo.prototype, "status", void 0);
92
+ __decorate([
93
+ Column({ nullable: true }),
94
+ Description("When true, a joining participant starts with their microphone muted."),
95
+ Nullable,
96
+ __metadata("design:type", Boolean)
97
+ ], VideoMeetingMongo.prototype, "forceMuteOnJoin", void 0);
98
+ __decorate([
99
+ Column({ nullable: true }),
100
+ Description("A hash of the password required to join, or unset when none is required. Never the plaintext."),
101
+ Nullable,
102
+ __metadata("design:type", String)
103
+ ], VideoMeetingMongo.prototype, "passwordHash", void 0);
104
+ __decorate([
105
+ Column({ nullable: true }),
106
+ Description("When true, nobody but the organizer joins directly - everyone else waits for the host to admit them."),
107
+ Nullable,
108
+ __metadata("design:type", Boolean)
109
+ ], VideoMeetingMongo.prototype, "waitingRoomEnabled", void 0);
89
110
  __decorate([
90
111
  Column({ nullable: true }),
91
112
  Description("Informational only: not used for any availability or conflict checking."),
@@ -1 +1 @@
1
- {"version":3,"file":"VideoMeetingMongo.js","sourceRoot":"","sources":["../../../../src/models/mongo/VideoMeetingMongo.ts"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"VideoMeetingMongo.js","sourceRoot":"","sources":["../../../../src/models/mongo/VideoMeetingMongo.ts"],"names":[],"mappings":";;;;;;;;;AAAA,+EAA+E;AAC/E,mEAAmE;AACnE,mCAAmC;AACnC,+EAA+E;AAC/E,OAAO,EAAE,gBAAgB,EAAE,MAAM,iBAAiB,CAAC;AACnD,OAAO,EAAE,eAAe,EAAE,aAAa,EAAE,eAAe,EAAE,qBAAqB,EAAE,MAAM,yBAAyB,CAAC;AACjH,OAAO,EAAE,iBAAiB,EAAE,MAAM,kBAAkB,CAAC;AACrD,OAAO,EAAgB,kBAAkB,EAAE,sBAAsB,EAAE,MAAM,aAAa,CAAC;AACvF,MAAM,EAAE,WAAW,EAAE,GAAG,aAAa,CAAC;AACtC,MAAM,EAAE,SAAS,EAAE,OAAO,EAAE,GAAG,eAAe,CAAC;AAC/C,MAAM,EAAE,MAAM,EAAE,MAAM,EAAE,KAAK,EAAE,GAAG,qBAAqB,CAAC;AACxD,MAAM,EAAE,QAAQ,EAAE,GAAG,gBAAgB,CAAC;AAEtC;;;;;;;;GAQG;AAwBI,IAAM,iBAAiB,GAAvB,MAAM,iBAAkB,SAAQ,eAAe;IAyDlD,YAAY,KAAkC;QAC1C,KAAK,CAAC,KAAK,CAAC,CAAC;QAvDV,eAAU,GAAW,EAAE,CAAC;QASxB,UAAK,GAAW,EAAE,CAAC;QAInB,eAAU,GAA2B,sBAAsB,CAAC,OAAO,CAAC;QAcpE,WAAM,GAAuB,kBAAkB,CAAC,SAAS,CAAC;QA8B7D,IAAI,KAAK,EAAE,CAAC;YACR,IAAI,CAAC,UAAU,GAAG,KAAK,CAAC,UAAU,KAAK,SAAS,CAAC,CAAC,CAAC,KAAK,CAAC,UAAU,CAAC,CAAC,CAAC,IAAI,CAAC,UAAU,CAAC;YACtF,IAAI,CAAC,gBAAgB,GAAG,kBAAkB,IAAI,KAAK,CAAC,CAAC,CAAC,KAAK,CAAC,gBAAgB,CAAC,CAAC,CAAC,IAAI,CAAC,gBAAgB,CAAC;YACrG,IAAI,CAAC,KAAK,GAAG,KAAK,CAAC,KAAK,KAAK,SAAS,CAAC,CAAC,CAAC,KAAK,CAAC,KAAK,CAAC,CAAC,CAAC,IAAI,CAAC,KAAK,CAAC;YAClE,IAAI,CAAC,UAAU,GAAG,KAAK,CAAC,UAAU,KAAK,SAAS,CAAC,CAAC,CAAC,KAAK,CAAC,UAAU,CAAC,CAAC,CAAC,IAAI,CAAC,UAAU,CAAC;YACtF,IAAI,CAAC,UAAU,GAAG,YAAY,IAAI,KAAK,CAAC,CAAC,CAAC,KAAK,CAAC,UAAU,CAAC,CAAC,CAAC,IAAI,CAAC,UAAU,CAAC;YAC7E,IAAI,CAAC,aAAa,GAAG,eAAe,IAAI,KAAK,CAAC,CAAC,CAAC,KAAK,CAAC,aAAa,CAAC,CAAC,CAAC,IAAI,CAAC,aAAa,CAAC;YACzF,IAAI,CAAC,MAAM,GAAG,KAAK,CAAC,MAAM,KAAK,SAAS,CAAC,CAAC,CAAC,KAAK,CAAC,MAAM,CAAC,CAAC,CAAC,IAAI,CAAC,MAAM,CAAC;YACtE,IAAI,CAAC,eAAe,GAAG,iBAAiB,IAAI,KAAK,CAAC,CAAC,CAAC,KAAK,CAAC,eAAe,CAAC,CAAC,CAAC,IAAI,CAAC,eAAe,CAAC;YACjG,IAAI,CAAC,YAAY,GAAG,cAAc,IAAI,KAAK,CAAC,CAAC,CAAC,KAAK,CAAC,YAAY,CAAC,CAAC,CAAC,IAAI,CAAC,YAAY,CAAC;YACrF,IAAI,CAAC,kBAAkB,GAAG,oBAAoB,IAAI,KAAK,CAAC,CAAC,CAAC,KAAK,CAAC,kBAAkB,CAAC,CAAC,CAAC,IAAI,CAAC,kBAAkB,CAAC;YAC7G,IAAI,CAAC,SAAS,GAAG,WAAW,IAAI,KAAK,CAAC,CAAC,CAAC,KAAK,CAAC,SAAS,CAAC,CAAC,CAAC,IAAI,CAAC,SAAS,CAAC;YACzE,IAAI,CAAC,OAAO,GAAG,SAAS,IAAI,KAAK,CAAC,CAAC,CAAC,KAAK,CAAC,OAAO,CAAC,CAAC,CAAC,IAAI,CAAC,OAAO,CAAC;QACrE,CAAC;IACL,CAAC;CACJ,CAAA;AAxEU;IAFN,MAAM,EAAE;IACR,WAAW,CAAC,gEAAgE,CAAC;;qDAC/C;AAKxB;IAHN,MAAM,CAAC,EAAE,QAAQ,EAAE,IAAI,EAAE,CAAC;IAC1B,WAAW,CAAC,mFAAmF,CAAC;IAChG,QAAQ;;2DACwB;AAI1B;IAFN,MAAM,EAAE;IACR,WAAW,CAAC,8BAA8B,CAAC;;gDAClB;AAInB;IAFN,MAAM,EAAE;IACR,WAAW,CAAC,6FAA6F,CAAC;;qDAChC;AAKpE;IAHN,MAAM,CAAC,EAAE,QAAQ,EAAE,IAAI,EAAE,CAAC;IAC1B,WAAW,CAAC,+EAA+E,CAAC;IAC5F,QAAQ;;qDACkB;AAKpB;IAHN,MAAM,CAAC,EAAE,QAAQ,EAAE,IAAI,EAAE,CAAC;IAC1B,WAAW,CAAC,gFAAgF,CAAC;IAC7F,QAAQ;;wDACqB;AAIvB;IAFN,MAAM,EAAE;IACR,WAAW,CAAC,8CAA8C,CAAC;;iDACK;AAK1D;IAHN,MAAM,CAAC,EAAE,QAAQ,EAAE,IAAI,EAAE,CAAC;IAC1B,WAAW,CAAC,sEAAsE,CAAC;IACnF,QAAQ;;0DACwB;AAK1B;IAHN,MAAM,CAAC,EAAE,QAAQ,EAAE,IAAI,EAAE,CAAC;IAC1B,WAAW,CAAC,+FAA+F,CAAC;IAC5G,QAAQ;;uDACoB;AAKtB;IAHN,MAAM,CAAC,EAAE,QAAQ,EAAE,IAAI,EAAE,CAAC;IAC1B,WAAW,CAAC,sGAAsG,CAAC;IACnH,QAAQ;;6DAC2B;AAK7B;IAHN,MAAM,CAAC,EAAE,QAAQ,EAAE,IAAI,EAAE,CAAC;IAC1B,WAAW,CAAC,yEAAyE,CAAC;IACtF,QAAQ;8BACU,IAAI;oDAAC;AAKjB;IAHN,MAAM,CAAC,EAAE,QAAQ,EAAE,IAAI,EAAE,CAAC;IAC1B,WAAW,CAAC,yEAAyE,CAAC;IACtF,QAAQ;8BACQ,IAAI;kDAAC;AAvDb,iBAAiB;IAvB7B,SAAS,CAAC,OAAO,CAAC;IAClB,MAAM,EAAE;IACR,iBAAiB,EAAE;IACnB,WAAW,CAAC,sEAAsE,CAAC;IACnF,KAAK,CAAC,sBAAsB,EAAE,CAAC,YAAY,CAAC,CAAC;IAC9C,iHAAiH;IACjH,oHAAoH;IACpH,oHAAoH;IACpH,4GAA4G;IAC5G,sHAAsH;IACtH,gHAAgH;;IAC/G,KAAK,CAAC,0BAA0B,EAAE,CAAC,YAAY,CAAC,EAAE,EAAE,MAAM,EAAE,IAAI,EAAE,MAAM,EAAE,IAAI,EAAE,CAAC;IACjF,KAAK,CAAC,6BAA6B,EAAE,CAAC,eAAe,CAAC,EAAE,EAAE,MAAM,EAAE,IAAI,EAAE,MAAM,EAAE,IAAI,EAAE,CAAC;IACvF,OAAO,CACJ;QACI,GAAG,EAAE,cAAc;QACnB,OAAO,EAAE;YACL,EAAE,YAAY,EAAE,WAAW,EAAE,OAAO,EAAE,EAAE,EAAE;YAC1C,EAAE,YAAY,EAAE,IAAI,EAAE,OAAO,EAAE,EAAE,EAAE;SACtC;KACJ,EACD,IAAI,CACP;;GACY,iBAAiB,CA2E7B"}
@@ -45,6 +45,9 @@ let VideoMeetingSQL = class VideoMeetingSQL extends BaseEntity {
45
45
  this.publicSlug = "publicSlug" in other ? other.publicSlug : this.publicSlug;
46
46
  this.organizerSlug = "organizerSlug" in other ? other.organizerSlug : this.organizerSlug;
47
47
  this.status = other.status !== undefined ? other.status : this.status;
48
+ this.forceMuteOnJoin = "forceMuteOnJoin" in other ? other.forceMuteOnJoin : this.forceMuteOnJoin;
49
+ this.passwordHash = "passwordHash" in other ? other.passwordHash : this.passwordHash;
50
+ this.waitingRoomEnabled = "waitingRoomEnabled" in other ? other.waitingRoomEnabled : this.waitingRoomEnabled;
48
51
  this.startTime = "startTime" in other ? other.startTime : this.startTime;
49
52
  this.endTime = "endTime" in other ? other.endTime : this.endTime;
50
53
  }
@@ -88,6 +91,24 @@ __decorate([
88
91
  Description("The current lifecycle state of this meeting."),
89
92
  __metadata("design:type", String)
90
93
  ], VideoMeetingSQL.prototype, "status", void 0);
94
+ __decorate([
95
+ Column({ nullable: true }),
96
+ Description("When true, a joining participant starts with their microphone muted."),
97
+ Nullable,
98
+ __metadata("design:type", Boolean)
99
+ ], VideoMeetingSQL.prototype, "forceMuteOnJoin", void 0);
100
+ __decorate([
101
+ Column({ nullable: true }),
102
+ Description("A hash of the password required to join, or unset when none is required. Never the plaintext."),
103
+ Nullable,
104
+ __metadata("design:type", String)
105
+ ], VideoMeetingSQL.prototype, "passwordHash", void 0);
106
+ __decorate([
107
+ Column({ nullable: true }),
108
+ Description("When true, nobody but the organizer joins directly - everyone else waits for the host to admit them."),
109
+ Nullable,
110
+ __metadata("design:type", Boolean)
111
+ ], VideoMeetingSQL.prototype, "waitingRoomEnabled", void 0);
91
112
  __decorate([
92
113
  Column({ nullable: true }),
93
114
  Description("Informational only: not used for any availability or conflict checking."),
@@ -1 +1 @@
1
- {"version":3,"file":"VideoMeetingSQL.js","sourceRoot":"","sources":["../../../../src/models/sql/VideoMeetingSQL.ts"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"VideoMeetingSQL.js","sourceRoot":"","sources":["../../../../src/models/sql/VideoMeetingSQL.ts"],"names":[],"mappings":"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"}