@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.cjs CHANGED
@@ -2604,6 +2604,10 @@ function personOf(identity) {
2604
2604
  const cut = value.indexOf(CONNECTION_SEPARATOR);
2605
2605
  return cut === -1 ? value : value.slice(0, cut);
2606
2606
  }
2607
+ function userIdOfIdentity(identity) {
2608
+ const person = personOf(identity);
2609
+ return person.startsWith("user:") ? person.slice("user:".length) : null;
2610
+ }
2607
2611
  function isGuestIdentity(identity) {
2608
2612
  return personOf(identity).startsWith("guest:");
2609
2613
  }
@@ -2844,6 +2848,7 @@ function minutesLeft(policy, startedAtMs, nowMs) {
2844
2848
  function mayAdmitNow(role, participants, policy) {
2845
2849
  if (role === "none") return false;
2846
2850
  if (role === "host" || role === "cohost") return true;
2851
+ if (role === "viewer") return false;
2847
2852
  const hostIn = participants.some((p) => p.role === "host" || p.role === "cohost");
2848
2853
  return !hostIn && policy.headlessPowers === "members_admit";
2849
2854
  }
@@ -4656,6 +4661,17 @@ function createMeetingSession(options) {
4656
4661
  }
4657
4662
  return identity.userId;
4658
4663
  }
4664
+ function requireAdmitterUser(operation) {
4665
+ const snapshot = store.snapshot();
4666
+ if (identity.userId === null || !mayAdmitNow(effectiveRole(snapshot), snapshot.participants, snapshot.livePolicy)) {
4667
+ throw new MeetError(
4668
+ "forbidden",
4669
+ `${operation}: this participant may not let people in`,
4670
+ "Only a host or co-host admits people, unless the meeting has no host in it and lets members admit."
4671
+ );
4672
+ }
4673
+ return identity.userId;
4674
+ }
4659
4675
  function requireMeeting(operation) {
4660
4676
  const meeting = store.snapshot().meeting;
4661
4677
  if (meeting === null) {
@@ -5062,7 +5078,7 @@ function createMeetingSession(options) {
5062
5078
  meetingChannel.requestBackfill("manual");
5063
5079
  },
5064
5080
  async admit(entry, admitted) {
5065
- const by = requireHostUser("meet.admit");
5081
+ const by = requireAdmitterUser("meet.admit");
5066
5082
  const meeting = requireMeeting("meet.admit");
5067
5083
  const row = await repository.decideAdmission({
5068
5084
  meetingId: meeting.id,
@@ -5080,7 +5096,7 @@ function createMeetingSession(options) {
5080
5096
  hint();
5081
5097
  },
5082
5098
  async undeny(entry) {
5083
- const by = requireHostUser("meet.undeny");
5099
+ const by = requireAdmitterUser("meet.undeny");
5084
5100
  const meeting = requireMeeting("meet.undeny");
5085
5101
  const row = await repository.decideAdmission({
5086
5102
  meetingId: meeting.id,
@@ -5093,7 +5109,7 @@ function createMeetingSession(options) {
5093
5109
  hint();
5094
5110
  },
5095
5111
  async admitAll() {
5096
- const by = requireHostUser("meet.admitAll");
5112
+ const by = requireAdmitterUser("meet.admitAll");
5097
5113
  const meeting = requireMeeting("meet.admitAll");
5098
5114
  const rows = await repository.admitAll(meeting.id, by);
5099
5115
  for (const row of rows) {
@@ -6134,21 +6150,35 @@ function heartbeatIntervalMs(roomSize) {
6134
6150
  function staleAfterMs(roomSize) {
6135
6151
  return heartbeatIntervalMs(roomSize) * 3 + 1e3;
6136
6152
  }
6153
+ function firstBeatAllowanceMs(roomSize) {
6154
+ return staleAfterMs(roomSize) * 2;
6155
+ }
6137
6156
  function createLivenessTracker(now) {
6138
6157
  const lastHeard = /* @__PURE__ */ new Map();
6158
+ const firstSeen = /* @__PURE__ */ new Map();
6139
6159
  const beats = /* @__PURE__ */ new Map();
6140
6160
  return {
6141
6161
  heard(who, beat) {
6142
6162
  lastHeard.set(who, now());
6163
+ firstSeen.delete(who);
6143
6164
  if (beat !== null) beats.set(who, beat);
6144
6165
  },
6166
+ expect(who) {
6167
+ if (!lastHeard.has(who) && !firstSeen.has(who)) firstSeen.set(who, now());
6168
+ },
6145
6169
  stale(present, roomSize) {
6146
6170
  const limit = staleAfterMs(roomSize);
6171
+ const firstLimit = firstBeatAllowanceMs(roomSize);
6147
6172
  const at = now();
6148
6173
  const out = /* @__PURE__ */ new Set();
6149
6174
  for (const who of present) {
6150
6175
  const heard = lastHeard.get(who);
6151
- if (heard !== void 0 && at - heard > limit) out.add(who);
6176
+ if (heard !== void 0) {
6177
+ if (at - heard > limit) out.add(who);
6178
+ continue;
6179
+ }
6180
+ const seen = firstSeen.get(who);
6181
+ if (seen !== void 0 && at - seen > firstLimit) out.add(who);
6152
6182
  }
6153
6183
  return out;
6154
6184
  },
@@ -6156,13 +6186,16 @@ function createLivenessTracker(now) {
6156
6186
  restartClock() {
6157
6187
  const at = now();
6158
6188
  for (const who of lastHeard.keys()) lastHeard.set(who, at);
6189
+ for (const who of firstSeen.keys()) firstSeen.set(who, at);
6159
6190
  },
6160
6191
  forget(who) {
6161
6192
  lastHeard.delete(who);
6193
+ firstSeen.delete(who);
6162
6194
  beats.delete(who);
6163
6195
  },
6164
6196
  clear() {
6165
6197
  lastHeard.clear();
6198
+ firstSeen.clear();
6166
6199
  beats.clear();
6167
6200
  }
6168
6201
  };
@@ -6928,6 +6961,13 @@ function createRoomEngine(options) {
6928
6961
  const previous = new Map(liveRoster.map((p) => [p.identity, p]));
6929
6962
  const present = new Set(list.map((p) => p.identity));
6930
6963
  const graceMs = connectionPolicy.reconnectGraceSeconds * 1e3;
6964
+ if (store.snapshot().phase === "in_call") {
6965
+ for (const [who, last] of previous) {
6966
+ if (last.isLocal || present.has(who) || departures.has(who)) continue;
6967
+ departures.set(who, null);
6968
+ 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.");
6969
+ }
6970
+ }
6931
6971
  for (const [who, reason] of departures) {
6932
6972
  const last = previous.get(who);
6933
6973
  const dropped = !saidBye.has(who) && (isUnannouncedDeparture(reason) || silent.has(who));
@@ -6943,6 +6983,9 @@ function createRoomEngine(options) {
6943
6983
  }
6944
6984
  departures.clear();
6945
6985
  liveRoster = list;
6986
+ for (const p of list) {
6987
+ if (!p.isLocal && (isGuestIdentity(p.identity) || userIdOfIdentity(p.identity) !== null)) liveness.expect(p.identity);
6988
+ }
6946
6989
  const localNow = list.find((p) => p.isLocal);
6947
6990
  if (localNow !== void 0 && !localNow.screenSharing && store.snapshot().share.state === "presenting") {
6948
6991
  store.patch({ share: NO_SHARE });
@@ -6963,7 +7006,7 @@ function createRoomEngine(options) {
6963
7006
  store.setParticipants(
6964
7007
  [...people.values()].map(({ entry }) => {
6965
7008
  const projected = projectParticipant(entry, joinedAt);
6966
- if (entry.reconnecting === true || !entry.isLocal && silent.has(entry.identity)) {
7009
+ if (entry.reconnecting === true || !entry.isLocal && silent.has(entry.identity) && !saidBye.has(entry.identity)) {
6967
7010
  return { ...projected, reconnecting: true, quality: "lost" };
6968
7011
  }
6969
7012
  const videoPaused = entry.isLocal ? store.snapshot().localNetwork.videoPaused : liveness.reported(entry.identity)?.vp === true;