@ai-matrx/meet 0.11.52 → 0.11.54

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/dist/react.js CHANGED
@@ -2277,6 +2277,10 @@ function personOf(identity) {
2277
2277
  const cut = value.indexOf(CONNECTION_SEPARATOR);
2278
2278
  return cut === -1 ? value : value.slice(0, cut);
2279
2279
  }
2280
+ function userIdOfIdentity(identity) {
2281
+ const person = personOf(identity);
2282
+ return person.startsWith("user:") ? person.slice("user:".length) : null;
2283
+ }
2280
2284
  function isGuestIdentity(identity) {
2281
2285
  return personOf(identity).startsWith("guest:");
2282
2286
  }
@@ -2517,6 +2521,7 @@ function minutesLeft(policy, startedAtMs, nowMs) {
2517
2521
  function mayAdmitNow(role, participants, policy) {
2518
2522
  if (role === "none") return false;
2519
2523
  if (role === "host" || role === "cohost") return true;
2524
+ if (role === "viewer") return false;
2520
2525
  const hostIn = participants.some((p) => p.role === "host" || p.role === "cohost");
2521
2526
  return !hostIn && policy.headlessPowers === "members_admit";
2522
2527
  }
@@ -4329,6 +4334,17 @@ function createMeetingSession(options) {
4329
4334
  }
4330
4335
  return identity.userId;
4331
4336
  }
4337
+ function requireAdmitterUser(operation) {
4338
+ const snapshot = store.snapshot();
4339
+ if (identity.userId === null || !mayAdmitNow(effectiveRole(snapshot), snapshot.participants, snapshot.livePolicy)) {
4340
+ throw new MeetError(
4341
+ "forbidden",
4342
+ `${operation}: this participant may not let people in`,
4343
+ "Only a host or co-host admits people, unless the meeting has no host in it and lets members admit."
4344
+ );
4345
+ }
4346
+ return identity.userId;
4347
+ }
4332
4348
  function requireMeeting(operation) {
4333
4349
  const meeting = store.snapshot().meeting;
4334
4350
  if (meeting === null) {
@@ -4735,7 +4751,7 @@ function createMeetingSession(options) {
4735
4751
  meetingChannel.requestBackfill("manual");
4736
4752
  },
4737
4753
  async admit(entry, admitted) {
4738
- const by = requireHostUser("meet.admit");
4754
+ const by = requireAdmitterUser("meet.admit");
4739
4755
  const meeting = requireMeeting("meet.admit");
4740
4756
  const row = await repository.decideAdmission({
4741
4757
  meetingId: meeting.id,
@@ -4753,7 +4769,7 @@ function createMeetingSession(options) {
4753
4769
  hint();
4754
4770
  },
4755
4771
  async undeny(entry) {
4756
- const by = requireHostUser("meet.undeny");
4772
+ const by = requireAdmitterUser("meet.undeny");
4757
4773
  const meeting = requireMeeting("meet.undeny");
4758
4774
  const row = await repository.decideAdmission({
4759
4775
  meetingId: meeting.id,
@@ -4766,7 +4782,7 @@ function createMeetingSession(options) {
4766
4782
  hint();
4767
4783
  },
4768
4784
  async admitAll() {
4769
- const by = requireHostUser("meet.admitAll");
4785
+ const by = requireAdmitterUser("meet.admitAll");
4770
4786
  const meeting = requireMeeting("meet.admitAll");
4771
4787
  const rows = await repository.admitAll(meeting.id, by);
4772
4788
  for (const row of rows) {
@@ -5809,21 +5825,35 @@ function heartbeatIntervalMs(roomSize) {
5809
5825
  function staleAfterMs(roomSize) {
5810
5826
  return heartbeatIntervalMs(roomSize) * 3 + 1e3;
5811
5827
  }
5828
+ function firstBeatAllowanceMs(roomSize) {
5829
+ return staleAfterMs(roomSize) * 2;
5830
+ }
5812
5831
  function createLivenessTracker(now) {
5813
5832
  const lastHeard = /* @__PURE__ */ new Map();
5833
+ const firstSeen = /* @__PURE__ */ new Map();
5814
5834
  const beats = /* @__PURE__ */ new Map();
5815
5835
  return {
5816
5836
  heard(who, beat) {
5817
5837
  lastHeard.set(who, now());
5838
+ firstSeen.delete(who);
5818
5839
  if (beat !== null) beats.set(who, beat);
5819
5840
  },
5841
+ expect(who) {
5842
+ if (!lastHeard.has(who) && !firstSeen.has(who)) firstSeen.set(who, now());
5843
+ },
5820
5844
  stale(present, roomSize) {
5821
5845
  const limit = staleAfterMs(roomSize);
5846
+ const firstLimit = firstBeatAllowanceMs(roomSize);
5822
5847
  const at = now();
5823
5848
  const out = /* @__PURE__ */ new Set();
5824
5849
  for (const who of present) {
5825
5850
  const heard = lastHeard.get(who);
5826
- if (heard !== void 0 && at - heard > limit) out.add(who);
5851
+ if (heard !== void 0) {
5852
+ if (at - heard > limit) out.add(who);
5853
+ continue;
5854
+ }
5855
+ const seen = firstSeen.get(who);
5856
+ if (seen !== void 0 && at - seen > firstLimit) out.add(who);
5827
5857
  }
5828
5858
  return out;
5829
5859
  },
@@ -5831,13 +5861,16 @@ function createLivenessTracker(now) {
5831
5861
  restartClock() {
5832
5862
  const at = now();
5833
5863
  for (const who of lastHeard.keys()) lastHeard.set(who, at);
5864
+ for (const who of firstSeen.keys()) firstSeen.set(who, at);
5834
5865
  },
5835
5866
  forget(who) {
5836
5867
  lastHeard.delete(who);
5868
+ firstSeen.delete(who);
5837
5869
  beats.delete(who);
5838
5870
  },
5839
5871
  clear() {
5840
5872
  lastHeard.clear();
5873
+ firstSeen.clear();
5841
5874
  beats.clear();
5842
5875
  }
5843
5876
  };
@@ -6603,6 +6636,13 @@ function createRoomEngine(options) {
6603
6636
  const previous = new Map(liveRoster.map((p) => [p.identity, p]));
6604
6637
  const present = new Set(list.map((p) => p.identity));
6605
6638
  const graceMs = connectionPolicy.reconnectGraceSeconds * 1e3;
6639
+ if (store.snapshot().phase === "in_call") {
6640
+ for (const [who, last] of previous) {
6641
+ if (last.isLocal || present.has(who) || departures.has(who)) continue;
6642
+ departures.set(who, null);
6643
+ report("warn", `meet: ${who} vanished from the room's roster with no departure event; held as reconnecting`, "The media layer dropped a participant silently; the tile is kept for the reconnect grace.");
6644
+ }
6645
+ }
6606
6646
  for (const [who, reason] of departures) {
6607
6647
  const last = previous.get(who);
6608
6648
  const dropped = !saidBye.has(who) && (isUnannouncedDeparture(reason) || silent.has(who));
@@ -6618,6 +6658,9 @@ function createRoomEngine(options) {
6618
6658
  }
6619
6659
  departures.clear();
6620
6660
  liveRoster = list;
6661
+ for (const p of list) {
6662
+ if (!p.isLocal && (isGuestIdentity(p.identity) || userIdOfIdentity(p.identity) !== null)) liveness.expect(p.identity);
6663
+ }
6621
6664
  const localNow = list.find((p) => p.isLocal);
6622
6665
  if (localNow !== void 0 && !localNow.screenSharing && store.snapshot().share.state === "presenting") {
6623
6666
  store.patch({ share: NO_SHARE });
@@ -6638,7 +6681,7 @@ function createRoomEngine(options) {
6638
6681
  store.setParticipants(
6639
6682
  [...people.values()].map(({ entry }) => {
6640
6683
  const projected = projectParticipant(entry, joinedAt);
6641
- if (entry.reconnecting === true || !entry.isLocal && silent.has(entry.identity)) {
6684
+ if (entry.reconnecting === true || !entry.isLocal && silent.has(entry.identity) && !saidBye.has(entry.identity)) {
6642
6685
  return { ...projected, reconnecting: true, quality: "lost" };
6643
6686
  }
6644
6687
  const videoPaused = entry.isLocal ? store.snapshot().localNetwork.videoPaused : liveness.reported(entry.identity)?.vp === true;